TCP通信でメッセージが返ってこない
解決済
回答 4
投稿
- 評価
- クリップ 0
- VIEW 1,525
TCP通信のやり取りをするプログラムを作りたいと思っています。
クライアント側はC#、サーバ側はCを使い、
「クライアントがメッセージを送り、サーバーはクライアントに"hello"と返す。これをクライアントが"exit"と送るまで繰り返す。」
というコンソールアプリを作りましたが、"exit"以外のメッセージを送る場合にクライアントがメッセージを受け取ることができず、そこから動かなくなってしまいます。
上記の問題の原因と、ちゃんとサーバーからのメッセージを受け取ることができるようにするにはどうすればよいのかをご教授願います。
コンソール表示
クライアント側
enter message:
abcde
(ここから動かない。ctrl+cで終了し、再実行)
enter message:
exit
response message: hello
end.
サーバー側
abcde
sent :hello
exit
sent :hello
ソースコード
//クライアント側
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class Client {
public static void Main() {
string sendMsg = "";
string host = "xxx.xxx.xxx.xxx";//IPアドレスは省略
int port = 7777;
TcpClient tcp = new TcpClient(host, port);
NetworkStream stream = tcp.GetStream();
MemoryStream ms = new MemoryStream();
byte[] resBytes = new byte[256];
int resSize = 0;
do {
Console.WriteLine("enter message: ");
sendMsg = Console.ReadLine()+"\0";
byte[] sendBytes = Encoding.UTF8.GetBytes(sendMsg);
Console.WriteLine("send message: " + sendMsg);
stream.Write(sendBytes, 0, sendBytes.Length);
do {
resSize = stream.Read(resBytes, 0, resBytes.Length);
if (resSize == 0) {
break;
}
ms.Write(resBytes, 0, resSize);
} while (stream.DataAvailable || resBytes[resSize - 1] != '\n');
string resMsg = Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
Console.WriteLine("response message: " + resMsg);
} while (sendMsg != "exit\0");
Console.WriteLine("end.");
Console.ReadLine();
ms.Close();
stream.Close();
tcp.Close();
}
}
//サーバー側
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
int sock0;
struct sockaddr_in addr;
struct sockaddr_in client;
int len;
int sock;
char buf[128];
char sendMsg[256] = "hello";
sock0 = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sock0, (struct sockaddr *)&addr, sizeof(addr));
do {
listen(sock0, 5);
len = sizeof(client);
sock = accept(sock0, (struct sockaddr *)&client, &len);
int recvMsglen;
recvMsglen = recv(sock, buf, sizeof(buf), 0);
printf("%s\n", buf);
write(sock, sendMsg, sizeof(sendMsg));
printf("sent :%s\n", sendMsg);
}while (strcmp(buf, "exit") != 0);
close(sock);
close(sock0);
return 0;
}
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
+2
サーバ側が以下の繰り返しになってますね
listen
accept
recv
write
writeの次はlistenですから、新しくクライアントが接続してこないとそこで永遠に止まってしまいます
非同期通信にする必要がありますが、ライブラリを使わないと面倒だと思います
私はC++ならboost::asio 使いましたが、これも楽とはいえまだ面倒です
C#ですがPhotonとかMagicOnionとか楽そうな印象がありました
あと気になる点は、writeの返り値はチェックしましょう
100byteを送信しても返り値が30なら、
残りの70byteは送信されていませんので再度送信する必要があります
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
checkベストアンサー
+1
クライアント側の
while (stream.DataAvailable || resBytes[resSize - 1] != '\n');
だと両方がFalseにならないとずっとwhile文を実行します。
resBytes[resSize - 1] != '\n'ですけどサーバ側の送信メッセージは'Hello'でなく'Hello\n'である必要があると思います。
exitを送ると終了する理由はおそらくですが、exitを送信することでサーバは終了します。
クライアントは接続先がなくなったため、
if (resSize == 0) {
break;
}
を通ってwhile文をbreakしたので正常に動作するように見えたわけです。
こういった処理をチェックするにはデバックモードを利用するか至るところにConsole.WriteLineを挿入しどこの処理が実行されているか見ると確認しやすいです。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
補足です。
サーバー側のrecvも
recvMsglen = recv(sock, buf, sizeof(buf), 0);
一度にすべてのデータを受け取れるとは限りません(今回はデータが小さいのでうまくいきますが)。
クライアント側がわざわざ"\0"文字を付けているので、
例えば'\0'が現れるまでrecvを繰り返す、といった処理が必要です。
※send/recvで送受信するデータは1対1にはなりません。send二回で送ったものがrecv1回ですべて受け取れたり、send1回で送ったものがrecv2回で受け取ることもあります。その辺を注意してプログラムを書く必要があります。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
解決しましたので、修正したソースコードを載せておきます。
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class Client {
public static void Main() {
string sendMsg = "";
string host = "xxx.xxx.xxx.xxx";
int port = 7777;
TcpClient tcp = new TcpClient(host, port);
NetworkStream stream = tcp.GetStream();
MemoryStream ms = new MemoryStream();
byte[] resBytes = new byte[256];
int resSize = 0;
do {
ms = new MemoryStream();
Console.WriteLine("enter message: ");
sendMsg = Console.ReadLine()+"\0";
byte[] sendBytes = Encoding.UTF8.GetBytes(sendMsg);
Console.WriteLine("send message: " + sendMsg);
stream.Write(sendBytes, 0, sendBytes.Length);
do {
resSize = stream.Read(resBytes, 0, resBytes.Length);
if (resSize == 0) {
break;
}
ms.Write(resBytes, 0, resSize);
Console.WriteLine("" + resSize);
} while (stream.DataAvailable && resBytes[resSize - 1] != '\0');
string resMsg = Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
//string resMsg = Encoding.UTF8.GetString(resBytes);
Console.WriteLine("response message: " + resMsg);
} while (sendMsg != "exit\0");
Console.WriteLine("end.");
Console.ReadLine();
ms.Close();
stream.Close();
tcp.Close();
}
}
//サーバー側
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
int sock0;
struct sockaddr_in addr;
struct sockaddr_in client;
int len;
int sock;
char buf[128];
//char sendMsg[256] = "hello";
int num = 0;
sock0 = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sock0, (struct sockaddr *)&addr, sizeof(addr));
//do {
listen(sock0, 5);
len = sizeof(client);
sock = accept(sock0, (struct sockaddr *)&client, &len);
do {
int recvMsglen;
recvMsglen = recv(sock, buf, sizeof(buf), 0);
printf("%s\n", buf);
char sendMsg[] = "hello";
write(sock, sendMsg, sizeof(sendMsg));
printf("sent :%s\n", sendMsg);
}while (strcmp(buf, "exit") != 0);
close(sock);
close(sock0);
return 0;
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.33%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2019/08/23 11:18
2019/08/23 11:46
2019/08/23 12:15
非同期の設計をしっかりする必要がありますが、死ぬほど面倒すぎて私は諦めました
2019/08/23 17:03