シンプルな非同期のclientHandlerを実行しているwinsock2を使用する単純なブロッキングサーバー/クライアントアプリケーションがあります。アイデアは、ロボットアプリケーションのキープアライブ記号としてオープンな接続を持つことです。C++自身のソケットサーバーとクライアントが8つのメッセージの後にエラー10054と10053で接続をリセットしました
ソケット(クライアント&サーバ)の初期化とそれらの間の接続の確立はうまくいきますが、8つのメッセージの後に受信側が接続を閉じます(エラー10053)ので、送信側でエラー10054が発生します。すべてのメッセージを手作業で(1分ごとに1回)送信するか、自動的に送信するかは問題になりません。相手側がメッセージの間で返信しても、結果は常に同じです。
メッセージは、メタ情報付きヘッダー(id、command、payloadLength in bytes)と、もしあればacutalペイロードの2つの部分で構成されています。困惑部分は次のようなものです。sizeof()が返すより1バイト以上送る必要があります。それ以外の場合は動作しません。
これまでのところ私が試した:ループ
- 読み出し部分のメッセージは、複数のクライアントハンドラと任意のコールバックを取り除くソケット
- への書き込み/読み込みの前に
select()
の出力を見て - 同じマシンと異なるマシンのクライアント/サーバーで実行中
- wiresharkを使用した生データの表示
受信者がRST
フラグを設定しているメッセージの制限まですべてが正常に見えました。クライアントとサーバーの両方の入出力バッファは65kbに設定されており、私のメッセージは近づかない。さらに、私はメッセージごとに巨大なデータセット(50kb)を送るときにも起こります。接続が終了すると、8つのメッセージが表示されます。
私は、ソケットの構成のどこかに明らかなエラーがあると仮定しますが、エラー10054は非常にあいまいであるため、ヘルプやヒントは非常に不便です。
ここには、サーバー/クライアントを構成し、メッセージを送受信するコードスニペットがあります。読みやすくするために私はここにすべてのエラーチェックを追加しないでください:クライアント用として
//Server
//Init
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
struct sockaddr_in config;
config.sin_family = AF_INET;
config.sin_addr.s_addr = INADDR_ANY;
config.sin_port = htons(8080);
Socket m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//set server to a state where it accepts incoming connections
bind(m_sock, (sockaddr *)&config, sizeof(sockaddr_in));
listen(m_sock, 100);
int c = sizeof(struct sockaddr_in);
//block until new client connects
Socket m_client = accept(m_sock, (sockaddr*)client, &c));
//handling and awaiting the client messages would have happend asyncronously with the m_client pointers swapped etc.
//receive data
struct msgHeader{
short id;
short command;
short payloadLength;
}
msgHeader* head = new msgHeader;
int error = 0;
do {
err = recv(*client, (char*)head, headerSize, SD_RECEIVE);
if (err == headerSize)
{
if (head->payloadLength > 0)
{
char* payload = new char[head->payloadLength]{0};
err = recv(*client, payload, head->payloadLength, SD_RECEIVE); // SERVER DIES HERE!!
if (err == head->payloadLength)
{
// callback is happening here
}
else
{
// error occured
}
}
}
} while (err > 0);
delete head;
closesocket(m_sock);
WSACleanup();
を:
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa));
m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
m_serverConfig.sin_addr.s_addr = inet_addr("127.0.0.1");
m_serverConfig.sin_family = AF_INET;
m_serverConfig.sin_port = htons("8080");
connect(m_sock, (struct sockaddr *)&m_serverConfig, sizeof(sockaddr_in));
std::string msg = "Hello from client";
msgHeader head{ 23, 0, msg.size() };
while(true)
{
send(m_sock, (char*)&head, sizeof(msgHeader) + 1, SD_SEND); //this +1 offset irretates me since I'm trying to send raw data. sizeof returns 6 which is ok only if I send 7 bytes the server receives 6 thus the complete header.
send(m_sock, msg.c_str(), msg.size() + 1, SD_SEND)); // CLIENT DIES HERE AFTER THE 8th iteration!!
}
//closesocket()... wsacleanup etc...
あなたが全体のコードが必要な場合は、私はレポを公開すると、リンクを投稿します。
これまでのところ、バグを探す場所を提案してくれてありがとう。
EDIT:クライアントとサーバー+(屋根裏プロジェクト)、それをコンパイルするのに必要なすべてのための コードはここで見つけることができます: Code
TCPストリームではなく、メッセージ、ベースのプロトコルです。 'err = recv(* client、(char *)head、headerSize、SD_RECEIVE);'は利用可能なものに依存して* 'headerSize'バイトまで*与えます。これはすべてローカルホストなので、あなたはおそらく幸運になるでしょうが、それには期待できません。 'SD_RECEIVE'はシャットダウン受信フラグです。これは 'recv'のための有効な入力ではありません。 'send'呼び出しで' SD_SEND'(シャットダウン送信)をします。 – user4581301
私は 'MSG_WAITALL'フラグも試しました。このフラグは' headerSize'が受信され、同じ結果が返されるまでブロックします。 Nontheless:最初の7つのメッセージすべてが正しく受信されるため、ここではこれが主要な問題ではありません。 – Stanislav
'MSG_WAITALL'はストリームであり、メッセージではないので、TCPでは動作しません。 – user4581301