利用規約の変更について

【現状】
VC#でソケット通信のソフトを作成しています。

【問題】
クライアント―サーバ型のソケット通信をしようとしているのですが、切断後すぐに再接続できないという問題が生じております。

【調査結果】
調べたところ、TCP/IPの状態遷移でアクティブクローズ側がTIME_WAIT状態になることが分かっています。TIME_WAITの設定値には意味合いがあり、漂流中の重複パケットの問題を回避するためや最後のACK再送のためであることは理解しております。

【やりたいこと】
今回は基本的に1:1通信でルータを挟まずにやりとりするネットワークなので、TIME_WAITを0、もしくは数msecにしたいと考えています。
※ 同じIPアドレス、ポート番号で接続したい為(ポート固定での再接続)

【自分で出した解決案の一つ】
どうもサーバ(Listenする側)からアクティブクローズすると、TIME_WAITは生じるもののクライアントから接続要求を出すと同じポートからでももう一度接続できてしまうというのは判明したので、必ずサーバから切断要求を出す仕様にすればTIME_WAITが0になるように思われます。
ただし、なぜ再接続できるのかは不明のため、すっきりしない(&環境や設定が異なると再接続できない可能性あり)

【質問】
(1) クライアント側からアクティブクローズする場合に、TIME_WAIT状態を1秒未満に設定する方法はありますか?(VC#で)
(2) サーバからアクティブクローズした場合に、なぜ再接続できてしまうのでしょうか?

以上の2点の質問に、どうかご回答のほどよろしくお願いいたします。

A 回答 (1件)

> どうもサーバ(Listenする側)からアクティブクローズすると、TIME_WAITは生じるもののクライアントから接続要求を出すと同じポートからでももう一度接続できてしまうというのは判



ちゃんと双方了解のもとで close() できなかったときのこと言ってるなら
int optval = 1;
if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval))
しとけという話ではなくて?

この回答への補足

SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
の両方を試しましたが、特に変化なしでした。
使い方がまずいのかと色々調べましたが、TIME_WAITとの関係性についてしっかりと記述しているサイトもなく、不明のままです。

補足日時:2015/01/14 17:04
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

QTIME_WAIT となったセッションを早く終了させる方法はありませんか?

webベースのロールプレイングゲームを管理している者です。
昔から、夕方から深夜にかけてなどつながりにくい状況になっておりますが、いままでデータベースの負荷とかいろんな問題を解決して現在にいたっております。最近のつながりにくい原因を調査したところ、接続数がものすごく増えてしまっているのが原因らしいとわかりました。しかし、netstat で見てみると、実際に接続している時間はすぐ終わり、そのあとの TIME_WAIT の状態が長いようなのです。この状態では接続はすでに終わっているはずですが、これを早く終わらせるにはどうしたらいいのでしょうか?

Aベストアンサー

>とやってみたのですが、確かにぜんぜん変化しませんでした。

確かに変化しませんねぇ。TIME_WAITについては。。。
やはり、JFにあるようにあくまでFIN_WAIT_2に絡む設定のようですね。なので#2の参考サイトの内容は微妙ですね。すみません。

ざっとCentOS4.3のkernelソース眺めた感じだと
include/net/tcp.h

#define TCP_TIMEWAIT_LEN (60*HZ)
#define TCP_FIN_TIMEOUT TCP_TIMEWAIT_LEN
となっていて、約60がデフォルトのようです。で他のソースからも二つは使い分けられているようだったので
#define TCP_TIMEWAIT_LEN (15*HZ)
としてkernelリビルドして入れ替えたらTIME_WAITステータスは約15秒で破棄されました。当然標準のnet.ipv4.tcp_fin_timeoutも短くなってましたが。(ちなみにnet.ipv4.tcp_tw_recycleは標準のまま)

ご参考まで。

>とやってみたのですが、確かにぜんぜん変化しませんでした。

確かに変化しませんねぇ。TIME_WAITについては。。。
やはり、JFにあるようにあくまでFIN_WAIT_2に絡む設定のようですね。なので#2の参考サイトの内容は微妙ですね。すみません。

ざっとCentOS4.3のkernelソース眺めた感じだと
include/net/tcp.h

#define TCP_TIMEWAIT_LEN (60*HZ)
#define TCP_FIN_TIMEOUT TCP_TIMEWAIT_LEN
となっていて、約60がデフォルトのようです。で他のソースからも二つは使い分けられているようだったので
#...続きを読む

Qネットワーク切断を検出するには?

Linux上で動作するTCP/IP通信アプリケーションが、LANケーブル抜けによるネットワーク切断を検知するにはどうしたらよいのでしょうか?

外部からコネクションを確立した後にケーブルが抜けたとき、Linux側でソケットをcloseしたいのです。今はこれができておらず、接続状態のまま(netstatでみるとESTABLISHED)になっています。複数の接続を許していないので、ケーブルを繋いだあとにTCP/IPで再接続できません。

試したこと
ソケットに対するioctl(2)でifreq.ifr_flagsのIFF_UPフラグをみたけど検知不可
切断時にselect(2)がエラーリターンするかと思ったがだめ

ケーブルが抜けたとき、カーネルが eth0: link down とログに出力するのでどこか(/proc , /sys以下)を参照すればよさそうな気がするんですが…

Aベストアンサー

HP-UXなので微妙に異なる可能性はありますが、こんな感じです。
http://docs.hp.com/ja/B2355-60129/TCP.7P.html

QC#で通信処理。応答がない場合、すぐエラーにしたい

VS2005、C#で通信処理をしています。
やりたいことは「接続後、データを渡してその返答データをもらう」です。

//サーバーに接続
Int32 port = 9999;
TcpClient client = new TcpClient(server, port);

//サーバーにメッセージを送信
Byte[] dataA = System.Text.Encoding.UTF8.GetBytes(message);
Byte[] dataB = Byte[128];
NetworkStream stream = client.GetStream();
stream.Write(dataA, 0, dataA.Length);
len = stream.Read(dataB, 0, dataB.Length)

client.Close();

ネットのサンプルを参考にさせてもらい、上のようなソースを作ることができたのですが、2つの疑問があります。

(1)接続時に指定したIPアドレスが存在しない場合、エラーが返ってくるのが遅いです。そういうものなのでしょうか。たとえば、即時返答などはできないのでしょうか。
(2)接続してデータを送っても、向こうからデータが返ってこなければずっと待機したままです。たとえばミリセカンドで切ることはできないのでしょうか。
もしくは指定のミリセカンド経過後強制的にエラー処理に飛ばす等はできないのでしょうか。

通信処理をやるのが初めてで定石がよくわからず、あれこれ試している状態です。
処理についてご存じの方、ご指南いただけたらと思います。参考になるサイトや検索キーワードだけでも教えていただけたら嬉しいです。
よろしくお願いします。

VS2005、C#で通信処理をしています。
やりたいことは「接続後、データを渡してその返答データをもらう」です。

//サーバーに接続
Int32 port = 9999;
TcpClient client = new TcpClient(server, port);

//サーバーにメッセージを送信
Byte[] dataA = System.Text.Encoding.UTF8.GetBytes(message);
Byte[] dataB = Byte[128];
NetworkStream stream = client.GetStream();
stream.Write(dataA, 0, dataA.Length);
len = stream.Read(dataB, 0, dataB.Length)

client.Close();

ネットのサンプ...続きを読む

Aベストアンサー

C#は知らないのでアドバイスのみ。
この辺は使用しているクラスにそういう機能があるかどうか。
なければ、サーバのタイムアウトを待つしかない。
たとえば(2)であれば
TcpClient.SendTimeout プロパティを設定しとけば
Send メソッドが正常に完了する前にタイムアウトが発生した場合、TcpClient は SocketException をスローするらしいですよ。
詳細は参考URLのmsdn参照。

参考URL:http://msdn.microsoft.com/ja-jp/library/system.net.sockets.tcpclient.sendtimeout(VS.80).aspx

QSocketのSend関数でのCLOSEの検知 [Linux]

Linux環境でSocket(dm:PF_INET,type:SOCK_STREAM)を使用しての、
Client&ServerプログラムをCで作成しているのですが、
そこでのSend関数の使い方についてご助力ください。

Client&Serverプログラムは下記のような動きをします。

[Client]
ServerへConnectした後、複数のDataを数秒間隔でServerへ
送信(send関数使用)します。受信(recvやread関数等)は、
一切行いません。

[Server]
ClientからのConnectを受け付けた後、Clientから受信(recv関数
使用)したDataを標準出力へ表示する。送信(sendやwrite関数
等)は、一切行いません。


さて、ここでもしClientプログラムがCloseを発行したり、マシン
DOWN等の理由でConnectionが切断され、Server側のSocketが
CLOSE_WAIT状態になった場合、Bufferに溜まっていたDataを
すべて受けきった後、recv関数が0を返してくれるので
相手が終了したことがわかります。

ここからが質問のMainです。

では、もしServerプログラムがCloseを発行したり、マシン
DOWN等の理由でConnectionが切断され、Client側のSocketが
CLOSE_WAIT状態になっても、CLOSE_WAIT直後のsend関数が
なぜか正常に処理されてしまいます。無論このDataは、
Server側は受け取りません。この次のsend関数実行時に
EPIPEが返ってくるので、ここでようやくSocketが切断された
ことが判ります。

これを何とかCLOSE_WAIT状態になった直後から、send関数で
切断を検知できるようにできないでしょうか。

よろしくお願いします。

以上

Linux環境でSocket(dm:PF_INET,type:SOCK_STREAM)を使用しての、
Client&ServerプログラムをCで作成しているのですが、
そこでのSend関数の使い方についてご助力ください。

Client&Serverプログラムは下記のような動きをします。

[Client]
ServerへConnectした後、複数のDataを数秒間隔でServerへ
送信(send関数使用)します。受信(recvやread関数等)は、
一切行いません。

[Server]
ClientからのConnectを受け付けた後、Clientから受信(recv関数
使用)したDataを標準出力へ表示する。送信(se...続きを読む

Aベストアンサー

#3です。
>完全な保証ではないのですが、”送れた/送れない”をきちんと
ログとして残す必要があるのです。

なるほど、そういう事情でしたか。それだと、それなりの信憑性が要求されますね。

>それと最初にご提案いただいたrecvをNonBlockモードで呼び出す
方法ですが、これだとrecvのリターン値は0が返ってこないでしょうか?

Linuxで以下のような、プログラムを組んで確認しました。

//送信前に、ノンブロッキングでrecvする。
ret = recv(sock,rbuf,sizeof(rbuf),MSG_DONTWAIT);
if (ret == -1 && errno == EAGAIN){
//正常なので送信可能
ret = send(sock,msg[i],sendlen,0);
if (ret != sendlen){
//送信エラーの処理
}
}else{
//切断検知時の処理
 // CLOSE_WAITになると ret=0が返る
}

サーバー側で切断時、直ちにrecvで戻り値=0となり、エラーの検知ができました。ret==-1 かつ errno==EAGAIN
であれば、回線は正常状態です。

#3です。
>完全な保証ではないのですが、”送れた/送れない”をきちんと
ログとして残す必要があるのです。

なるほど、そういう事情でしたか。それだと、それなりの信憑性が要求されますね。

>それと最初にご提案いただいたrecvをNonBlockモードで呼び出す
方法ですが、これだとrecvのリターン値は0が返ってこないでしょうか?

Linuxで以下のような、プログラムを組んで確認しました。

//送信前に、ノンブロッキングでrecvする。
ret = recv(sock,rbuf,sizeof(rbuf),MSG_DONTWAIT);
if (ret == -...続きを読む

QTCP/IP通信でのコネクションロスト(ソケットエラー)の検知について

インターネット上での対戦ゲームの作成を行なっているところです。
サーバーを挟んでクライアント間で双方向の通信を行なっています。

 A → サーバー → B
 A ← サーバー ← B

TCP/IPは信頼性の高い通信方法ということで、データ抜けなどは
心配しなくても良いと聞いています。
もし、データ抜けなどが発生した場合はコネクションロストの状態になると。

実際のプログラミングではソケットを使用しています。
コネクションロストが起きると、ソケットエラーかソケットクローズで検知できます。


実際に例えばクライアントAを強制終了させるとサーバーはただちに
ソケットクローズを検知します。


ここで、問題はデータを送っても届かないのに、
ソケットエラーもソケットクローズも起きない状態が発生することです。
これはインターネットの経路上になにか問題が発生したと考えていますが
このような状態はタイムアウトなどで監視する以外に検知する方法はないのでしょうか?


クライアントはウィンドウズでVB6.0のwinsockを、
サーバーはLinuxを使用しています。

なにかアドバイスをいただければありがたいです。

インターネット上での対戦ゲームの作成を行なっているところです。
サーバーを挟んでクライアント間で双方向の通信を行なっています。

 A → サーバー → B
 A ← サーバー ← B

TCP/IPは信頼性の高い通信方法ということで、データ抜けなどは
心配しなくても良いと聞いています。
もし、データ抜けなどが発生した場合はコネクションロストの状態になると。

実際のプログラミングではソケットを使用しています。
コネクションロストが起きると、ソケットエラーかソケットクローズで...続きを読む

Aベストアンサー

>>これはインターネットの経路上になにか問題が発生したと考えていますが
>>このような状態はタイムアウトなどで監視する以外に検知する方法はないのでしょうか?

多くのアプリは、タイムアウトで処理しているようです。例えば、経路途中のどこかのLANケーブルが抜けて通信が失敗した場合、すぐにエラーを検出して異常になるよりも、LANケーブルを差し込んだら、そのまま継続してくれるほうが嬉しいわけですからね。

もし、デフォルトのタイムアウトがいやなら、定期的に信号をやりとりする仕組みを組み込めばいいと思います。ただし、そのやりとり自体もタイムアウトになる可能性があるのと、連番を振って管理するなどしてエラー時の再送をやる場合、考慮無くやると、アホな結果を生む可能性もあるので、注意が必要です。このあたりの話題はソケット関係の情報を検索すると出てくると思います。

あまり厳密にやると複雑化する気もするので、シンプルにタイムアウトでの対処がいいかなと思います。

Qc# ネットワークプログラム

c#でネットワークプログラムを作る際、
SocketクラスとTcpListener・TcpClientクラスのどちらで
作るのが良いのでしょうか。

やりたいことは、サーバーとクライアントを接続して送受信させるだけです。

Aベストアンサー

No.1です。

回答続編です。
とはいえ、こちらも難航中ですが。
(何となくTCP-Clientは判ってきたかなという感じですね)


>1秒間隔でポーリングして受信がある場合は受信を送信処理が
>ある場合は送信処理を1つのコネクションでさせる場合は
>どうすればいいのでしょうか。(c言語のselect関数相当)

…確か、Socketの方にはSelectを持っていた気がします。
 調べてみてください。
 しかし、一般的にポートは6万個しかありませんので。
 大規模なアクセスをも想定する場合は、1メソッドでポイと
 捨ててみてもいいような気もします。
 でないとポートが利用人数分消費されます。
 TCP/IPはどうも、永続的なセッション情報を元来あまり保証し
 なさそうな雰囲気ですので、(FrameworkのTCP-Clientは
 低レイヤーの切断情報などステータスをうまく取れない模様。)
 きっと[Data.Length][Separetor][Data] というような、
 RS-232Cのようなベタな「俺プロトコル」を適当につくり、
 TimeOutベースで審査&そのたびにスレッドを捨てるという
 やりかたでもいいんじゃないかなとも思えます。

>また、お勧めのサイトとかありましたら教えてください。
 当方の情報源は。
 1.VS2008のMSDN(ネットのは古い場合がある)
 2.C#500の技とかいう書籍
 3.海外のWebサイト
 ですね。
 日本は情報があまり見当たりません。
 Code Projectというサイトあたりを中心に物色しています。

私は、ClickOnceでTCP-Clientを配布して使えないかなという
実験中で。今日のところは、HTTPサーバとおしゃべりできた程度
です…

No.1です。

回答続編です。
とはいえ、こちらも難航中ですが。
(何となくTCP-Clientは判ってきたかなという感じですね)


>1秒間隔でポーリングして受信がある場合は受信を送信処理が
>ある場合は送信処理を1つのコネクションでさせる場合は
>どうすればいいのでしょうか。(c言語のselect関数相当)

…確か、Socketの方にはSelectを持っていた気がします。
 調べてみてください。
 しかし、一般的にポートは6万個しかありませんので。
 大規模なアクセスをも想定する場合は、1メソッドで...続きを読む

Qpingでポートの指定

pingでIPアドレスを指定して、通信できるかどうかというのは
よく使いますが、pingでポートを指定して応答するかどうかは調べられるのでしょうか?

よろしくお願いします

Aベストアンサー

pingを含むICMPというプロトコルは、OSIの7レイヤで言うところのL2(同一セグメント内通信)とL3(IPルーティングされた通信)の両方にまたがる、ちょっと珍しいプロトコルです。

IPアドレスは指定できますが、別サブネットに属するIPアドレスに到達できればL3通信、できなければゲートウェイと呼ばれる同一サブネットに属する中継装置からの回答を得るという点でL2(MAC通信ではなく、同一セグメント内通信という意味)通信です。

ポート番号はL4で使用されるアドレスですから、L4機能の疎通確認はping(を含むICMP)ではできません。

FTPの疎通確認であれば、クライアントからサーバに対するTCP/21通信(FTP-CMD)が可能であること(サーバからクライアントへのTCP/21からの応答を含む)+サーバからクライアントに対するTCP/20通信(FTP-DATA)が可能であること(クライアントからサーバへのTCP/21からの応答を含む)が必要でしょう。

監視ソフトによるものであれば、
・クライアントからサーバへのログイン(TCP/21)
・クライアントからサーバへのlsの結果(TCP/20)
で確認すればよいでしょう。

pingを含むICMPというプロトコルは、OSIの7レイヤで言うところのL2(同一セグメント内通信)とL3(IPルーティングされた通信)の両方にまたがる、ちょっと珍しいプロトコルです。

IPアドレスは指定できますが、別サブネットに属するIPアドレスに到達できればL3通信、できなければゲートウェイと呼ばれる同一サブネットに属する中継装置からの回答を得るという点でL2(MAC通信ではなく、同一セグメント内通信という意味)通信です。

ポート番号はL4で使用されるアドレスですから、L4機能の疎通確認はping(を含む...続きを読む

Qsocket: recvはいつ,どれだけ受け取るのか?

 現在,参考書にしたがってC++でソケットプログラミングを書いています.

 sendとrecvを非同期にするために,本では select関数やWSAAsyncSelect関数などを利用していて,実際,本のとおりに書いて上手く動いています.

 ここで伺いたいのですが,recvは,どうやって「データが届いたか」を知るのでしょうか.

 同期ならば,トランシーバでの会話のように送信側が「どうぞ」といって送受信を交代させることができますが,非同期ならばそれができません.

 NICとかが,プログラムに「届いたぞ!( or これから届くぞ!)」と教えてくれるのでしょうか.あるいは逆に,プログラムがNICに「届いてる?」と聞いているのでしょうか.仮に,ここに書いたような方法で届いたことが分かったとしても,どれくらい受け取ればいいかは分かりません(それも併せて教えてもらっているのでしょうか.データを送るときには,どれだけ送ればいいか分かりますよね.受信するときはどうしてるのかを知りたいと思っています).

Aベストアンサー

Linux しか知らないので Linux で説明をします。

NIC が通信パケットを受け取ると割り込みが発生し、CPU は割り込みを受け付けて、対応するデバイスドライバを起動します。この時、ドライバはソケットバッファと呼ばれる構造体にパケットの中身をコピーして、Linux カーネルの本体に渡し、そこで TCP 等の上位プロトコル処理が行われます。

一方、ユーザプログラムの方は、 select() なり read() で待っている訳ですが、OS はもちろんプロセスが何を待っているかを知っているので、対応する待ちの条件が満たされると、この場合は select() や read() が、抜けてくる(return する)訳です。

という事で、ユーザのプログラムは select() なり read() なりで受信データを「待つ」ことが必要です。もちろん select() や read() が呼ばれた時点で既に受信しているのならば、それらは直ぐに帰ってきます。read() や recv() はデータが届いた事を知る、というよりは、届いているかチェックして、まだ届いていなければ届くまで待つ(read() が抜けてこない)という処理になります。また NIC とユーザプログラムが直接やり取りをするのではなく、間にバッファがあって、対応するソケットのデータがある(受信済み)/ないか(未受信)、という問い合わせを行っているだけです。

ソケットの場合、データの送受信は非同期であり、送受信のタイミングのずれは(ソケット)バッファである程度吸収されます。もちろん、送受信バッファが満杯になった場合は流量制御が働いて、結果的に送信側の write() や send() が待ちに入ることになります。

Linux (Unix) のソケットの受信では、read() 等で指定されたバッファが常に満杯で返されるとは限らない設計になっています。つまり、その時に受信しているデータを返すだけなので、read() で返されたバイト数を必ず見ないと間違った動きになるので注意してください。

Linux しか知らないので Linux で説明をします。

NIC が通信パケットを受け取ると割り込みが発生し、CPU は割り込みを受け付けて、対応するデバイスドライバを起動します。この時、ドライバはソケットバッファと呼ばれる構造体にパケットの中身をコピーして、Linux カーネルの本体に渡し、そこで TCP 等の上位プロトコル処理が行われます。

一方、ユーザプログラムの方は、 select() なり read() で待っている訳ですが、OS はもちろんプロセスが何を待っているかを知っているので、対応する待ちの条件が満...続きを読む

QC# ソケット通信でデータ受信時の欠損について

Visualstudio 2013 を使用して C# で開発を行っています。

Socket Classを使用してデータの送受信をするプログラムを作成しているのですが、
非同期でデータを受信する際に取りこぼしなくデータを受信させる方法で悩んでいます。

現在行っているのは、
 socket.BeginReceiveでデータ受信時のコールバック関数を登録し受信開始。

 データ受信時のコールバック関数の中で
 len = socket.EndReceive(ar);
 byte[] rcvBuff = (byte[])ar.AsyncState;
 でデータとサイズを取得

 再度socket.BeginReceiveで受信開始。

としています。

悩んでいるのは EndReceive から、二回目のBeginReceiveまでの間に送られたデータを取りこぼしてしまうのではないかということです。


認識として間違っていたら指摘頂きたいです。
また、別の方法で非同期で取りこぼしなく受信できるやり方が有りましたらヒントでもよいので教えていただけたらと思います。

よろしくお願いします。

Visualstudio 2013 を使用して C# で開発を行っています。

Socket Classを使用してデータの送受信をするプログラムを作成しているのですが、
非同期でデータを受信する際に取りこぼしなくデータを受信させる方法で悩んでいます。

現在行っているのは、
 socket.BeginReceiveでデータ受信時のコールバック関数を登録し受信開始。

 データ受信時のコールバック関数の中で
 len = socket.EndReceive(ar);
 byte[] rcvBuff = (byte[])ar.AsyncState;
 でデータとサイズを取得

 再度socket.BeginReceiveで受...続きを読む

Aベストアンサー

> 悩んでいるのは EndReceive から、
> 二回目のBeginReceiveまでの間に送られたデータを取りこぼしてしまうのではないかということです。
大丈夫よん。

プログラムがBeginReceive~EndReceiveでひとかたまりの受信データブロックを取得するのはあくまで「同じコンピューターのOSから」なので、その時「同じコンピューターのOS」は相手からのデータ送信をどんどこどんどこ受け付けている。
そもそも最初のBeginReceiveをしなくてもコネクションが確立された瞬間からOSは相手からデータを受け取っているのだ。

これは、次のようなイメージだ。
(1)あなたは一階に総合受付がある雑居ビルで仕事をしてる。(あなた=プログラム、総合受付=NIC)
(2)一階の総合受付の中にあなた宛の郵便受けを置いたとする。(Socket#Listen、Socket#Accept)
(3)郵便屋さんはそこにあなた宛の郵便をどんどこどんどこ持ってくる。
(4)あなたは部下に「郵便受けに何かないか見てこい。何もなかったら何か来るまでそこで見張ってろ。郵便が有ったら報告せよ。」という。(BeginReceive)
(5)部下は降りていった時に郵便があればそのまま持って上がる。無ければ来るまで待つ。(コールバック)
(6)あなたは部下から郵便を受け取って再び(4)を命じる。(EndReceive、AsyncState、BeginReceive)

ここで、(3)と(4)~(6)は非同期にかつ独立して行われる。あなたや部下が郵便を取りに行こうが取りにいくまいが郵便屋さんは郵便を持ってきて便受けに入れるだけだ。具体的には(6)でEndReceiveとBegenReceiveの間に郵便屋さんが来たとしてもそれは便受けに郵便を入れるだけだ。
もちろん、あなたのプログラムが終了しようとしている時に郵便受けに溜まってるのにあなたが読まなかった郵便はプログラム終了(というかSocket#Close)とともにロストする。
TCPはこの郵便受けがぱんぱんになってしまったら郵便屋さん(というか郵便を送りつけてくる相手)にちょっと待って貰うとかそういう制御もしている。

イメージ的にはこんなイメージであなたはBeginReceiveとEndReceiveを実行するタイミングによる伝送データロストを気にする必要はない。もし本当にそんなちょっとしたタイミングの差でそんな事が起こるんならMicrosoftさんだってそんなコンポーネント作らんじゃろ。

> 悩んでいるのは EndReceive から、
> 二回目のBeginReceiveまでの間に送られたデータを取りこぼしてしまうのではないかということです。
大丈夫よん。

プログラムがBeginReceive~EndReceiveでひとかたまりの受信データブロックを取得するのはあくまで「同じコンピューターのOSから」なので、その時「同じコンピューターのOS」は相手からのデータ送信をどんどこどんどこ受け付けている。
そもそも最初のBeginReceiveをしなくてもコネクションが確立された瞬間からOSは相手からデータを受け取っているのだ。

こ...続きを読む

QC# シリアル通信でデータ受信時の欠損について

Visualstudio 2013 を使用して C# で開発を行っています。

SerialPort Classを使用してデータの送受信をするプログラムを作成しているのですが、
非同期でデータを受信する際にどうしてもうまくデータを取得出来ません。

5Byteのデータは正常に取得できるのですが、
その直後にくる40Byteのデータは、真ん中あたりの10数Byteや最後の10数Byteしか取れません。


serialPort.DataReceived に登録したイベント関数の中身です。

--------------------------------------------------------------------------------------
private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
bytesRead = 0;
// Initialize a buffer to hold the received data
byte[] buffer = new byte[this.serialPort.ReadBufferSize];

try
{
bytesRead = this.serialPort.Read(buffer, 0, buffer.Length);
if (true == serialPort.IsOpen)
{
serialPort.DiscardInBuffer();//受信バッファをクリアする
}

}
catch (Exception ex)
{
DataLog.Exception(ex);
}

//派生クラス用の処理
DeviceClassEventArgs _DeviceClassEventArgs = new DeviceClassEventArgs(buffer, bytesRead);
DeviceClassEvent(this, _DeviceClassEventArgs);
}
--------------------------------------------------------------------------------------

ネットの情報を参考に、
ReceivedBytesThreshold の値を期待するデータ量に逐一変えることで
とりあえず正常に取ることが出来たのですが、これでいいのでしょうか?
期待するデータ量がわからなかった場合は使えないのかなとも思います。

データが欠損してしまう理由、
上記の対処法以外の一般的な対処法など有りましたら教えて下さい。

その他参考になるページ等ありましたら教えていただけると大変助かります。

Visualstudio 2013 を使用して C# で開発を行っています。

SerialPort Classを使用してデータの送受信をするプログラムを作成しているのですが、
非同期でデータを受信する際にどうしてもうまくデータを取得出来ません。

5Byteのデータは正常に取得できるのですが、
その直後にくる40Byteのデータは、真ん中あたりの10数Byteや最後の10数Byteしか取れません。


serialPort.DataReceived に登録したイベント関数の中身です。

-------------------------------------------------------------------------------...続きを読む

Aベストアンサー

DataReceivedイベントが発生したときでも、
シリアルポートへの受信はまだ継続している可能性があるので
不用意にバッファクリアしてはいけない。
非同期の受信処理は、何かと難しいのです。

private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  bytesRead = 0;
  // Initialize a buffer to hold the received data
  byte[] buffer = new byte[this.serialPort.ReadBufferSize];

  try
  {
    //bytesRead = this.serialPort.Read(buffer, 0, buffer.Length);
    //if (true == serialPort.IsOpen)
    //{
    // serialPort.DiscardInBuffer();//受信バッファをクリアする
    //}

    // 受信バッファにデータがなくなるまで繰り返し読込む
    while (true)
    {
      if (0 == serialPort.BytesToRead)
      {
        break;
      }
      buffer[bytesRead] = (byte)serialPort.ReadByte();
      bytesRead++;
      System.Threading.Thread.Sleep(0);

      // シリアルポートの受信バッファには、
      // ・必要なブロックの途中から受信している。
      // ・次のブロックの先頭部分も受信されている。
      // 可能性があるので、ここで必要なブロックだけRead()できたことを確認する。
      if (必要なブロックが正常に読めたか確認する関数())
      {
        break;
      }
    }
  }
  catch (Exception ex)
  {
    DataLog.Exception(ex);
  }

  //派生クラス用の処理
  DeviceClassEventArgs _DeviceClassEventArgs = new DeviceClassEventArgs(buffer, bytesRead);
  DeviceClassEvent(this, _DeviceClassEventArgs);
}

DataReceivedイベントが発生したときでも、
シリアルポートへの受信はまだ継続している可能性があるので
不用意にバッファクリアしてはいけない。
非同期の受信処理は、何かと難しいのです。

private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  bytesRead = 0;
  // Initialize a buffer to hold the received data
  byte[] buffer = new byte[this.serialPort.ReadBufferSize];

  try
  {
    //bytesRead = this.serialPort.Read(buffer, 0, buffer.Le...続きを読む


人気Q&Aランキング

おすすめ情報