質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

サーバ

サーバは、 クライアントサーバモデルにおいてクライアントからの要求に対し 何らかのサービスを提供するプログラムを指す言葉です。 また、サーバーソフトウェアを稼動させているコンピュータ機器そのもののことも、 サーバーと呼ぶ場合もあります。

Q&A

解決済

4回答

2615閲覧

TCP通信でメッセージが返ってこない

kaitorisenkou

総合スコア28

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

サーバ

サーバは、 クライアントサーバモデルにおいてクライアントからの要求に対し 何らかのサービスを提供するプログラムを指す言葉です。 また、サーバーソフトウェアを稼動させているコンピュータ機器そのもののことも、 サーバーと呼ぶ場合もあります。

0グッド

0クリップ

投稿2019/08/22 17:10

TCP通信のやり取りをするプログラムを作りたいと思っています。

クライアント側はC#、サーバ側はCを使い、
「クライアントがメッセージを送り、サーバーはクライアントに"hello"と返す。これをクライアントが"exit"と送るまで繰り返す。」
というコンソールアプリを作りましたが、"exit"以外のメッセージを送る場合にクライアントがメッセージを受け取ることができず、そこから動かなくなってしまいます。

上記の問題の原因と、ちゃんとサーバーからのメッセージを受け取ることができるようにするにはどうすればよいのかをご教授願います。

###コンソール表示
クライアント側

enter message: abcde (ここから動かない。ctrl+cで終了し、再実行) enter message: exit response message: hello end.

サーバー側

abcde sent :hello exit sent :hello

###ソースコード

C#

1//クライアント側 2using System; 3using System.IO; 4using System.Net; 5using System.Net.Sockets; 6using System.Text; 7 8public class Client { 9 public static void Main() { 10 string sendMsg = ""; 11 string host = "xxx.xxx.xxx.xxx";//IPアドレスは省略 12 int port = 7777; 13 14 TcpClient tcp = new TcpClient(host, port); 15 NetworkStream stream = tcp.GetStream(); 16 17 MemoryStream ms = new MemoryStream(); 18 byte[] resBytes = new byte[256]; 19 int resSize = 0; 20 21 do { 22 Console.WriteLine("enter message: "); 23 sendMsg = Console.ReadLine()+"\0"; 24 byte[] sendBytes = Encoding.UTF8.GetBytes(sendMsg); 25 Console.WriteLine("send message: " + sendMsg); 26 stream.Write(sendBytes, 0, sendBytes.Length); 27 28 do { 29 resSize = stream.Read(resBytes, 0, resBytes.Length); 30 if (resSize == 0) { 31 break; 32 } 33 ms.Write(resBytes, 0, resSize); 34 } while (stream.DataAvailable || resBytes[resSize - 1] != '\n'); 35 36 string resMsg = Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); 37 Console.WriteLine("response message: " + resMsg); 38 } while (sendMsg != "exit\0"); 39 40 Console.WriteLine("end."); 41 Console.ReadLine(); 42 43 ms.Close(); 44 stream.Close(); 45 tcp.Close(); 46 } 47}

C

1//サーバー側 2#include <stdio.h> 3#include <string.h> 4#include <sys/types.h> 5#include <sys/socket.h> 6#include <netinet/in.h> 7 8int main() 9{ 10 int sock0; 11 struct sockaddr_in addr; 12 struct sockaddr_in client; 13 int len; 14 int sock; 15 char buf[128]; 16 char sendMsg[256] = "hello"; 17 18 sock0 = socket(AF_INET, SOCK_STREAM, 0); 19 20 addr.sin_family = AF_INET; 21 addr.sin_port = htons(7777); 22 addr.sin_addr.s_addr = INADDR_ANY; 23 24 bind(sock0, (struct sockaddr *)&addr, sizeof(addr)); 25 26 do { 27 listen(sock0, 5); 28 29 len = sizeof(client); 30 sock = accept(sock0, (struct sockaddr *)&client, &len); 31 32 int recvMsglen; 33 recvMsglen = recv(sock, buf, sizeof(buf), 0); 34 printf("%s\n", buf); 35 36 write(sock, sendMsg, sizeof(sendMsg)); 37 printf("sent :%s\n", sendMsg); 38 39 }while (strcmp(buf, "exit") != 0); 40 41 close(sock); 42 close(sock0); 43 44 return 0; 45}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

サーバ側が以下の繰り返しになってますね
listen
accept
recv
write

writeの次はlistenですから、新しくクライアントが接続してこないとそこで永遠に止まってしまいます

非同期通信にする必要がありますが、ライブラリを使わないと面倒だと思います
私はC++ならboost::asio 使いましたが、これも楽とはいえまだ面倒です
C#ですがPhotonとかMagicOnionとか楽そうな印象がありました

あと気になる点は、writeの返り値はチェックしましょう
100byteを送信しても返り値が30なら、
残りの70byteは送信されていませんので再度送信する必要があります

投稿2019/08/22 20:56

izmktr

総合スコア2856

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yumetodo

2019/08/23 02:18

boost.beastとboost.asioの組み合わせで多少は楽になったと思いますがね・・・、C#とかNode.jsで書きたい気持ち
kaitorisenkou

2019/08/23 02:46

一度の接続で何回もやり取りはできない、という事でよろしいでしょうか?
izmktr

2019/08/23 03:15

ちがいます、あなたの書き方が悪いせいで何度もやり取りできない設計になっています 非同期の設計をしっかりする必要がありますが、死ぬほど面倒すぎて私は諦めました
kaitorisenkou

2019/08/23 08:03

listenとacceptをループから外すことで無事解決しました!ありがとうございました!
guest

0

補足です。

サーバー側のrecvも

recvMsglen = recv(sock, buf, sizeof(buf), 0);

一度にすべてのデータを受け取れるとは限りません(今回はデータが小さいのでうまくいきますが)。
クライアント側がわざわざ"\0"文字を付けているので、
例えば'\0'が現れるまでrecvを繰り返す、といった処理が必要です。

※send/recvで送受信するデータは1対1にはなりません。send二回で送ったものがrecv1回ですべて受け取れたり、send1回で送ったものがrecv2回で受け取ることもあります。その辺を注意してプログラムを書く必要があります。

投稿2019/08/23 07:07

編集2019/08/23 07:56
takabosoft

総合スコア8356

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

クライアント側の
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を挿入しどこの処理が実行されているか見ると確認しやすいです。

投稿2019/08/23 03:02

bsk

総合スコア174

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kaitorisenkou

2019/08/23 08:02

回答ありがとうございます!クライアント側の問題が解決しました!
guest

0

解決しましたので、修正したソースコードを載せておきます。

C#

1using System; 2using System.IO; 3using System.Net; 4using System.Net.Sockets; 5using System.Text; 6 7public class Client { 8 public static void Main() { 9 string sendMsg = ""; 10 string host = "xxx.xxx.xxx.xxx"; 11 int port = 7777; 12 13 TcpClient tcp = new TcpClient(host, port); 14 NetworkStream stream = tcp.GetStream(); 15 16 MemoryStream ms = new MemoryStream(); 17 byte[] resBytes = new byte[256]; 18 int resSize = 0; 19 20 do { 21 ms = new MemoryStream(); 22 Console.WriteLine("enter message: "); 23 sendMsg = Console.ReadLine()+"\0"; 24 byte[] sendBytes = Encoding.UTF8.GetBytes(sendMsg); 25 Console.WriteLine("send message: " + sendMsg); 26 stream.Write(sendBytes, 0, sendBytes.Length); 27 28 do { 29 resSize = stream.Read(resBytes, 0, resBytes.Length); 30 if (resSize == 0) { 31 break; 32 } 33 ms.Write(resBytes, 0, resSize); 34 Console.WriteLine("" + resSize); 35 } while (stream.DataAvailable && resBytes[resSize - 1] != '\0'); 36 37 string resMsg = Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); 38 //string resMsg = Encoding.UTF8.GetString(resBytes); 39 Console.WriteLine("response message: " + resMsg); 40 } while (sendMsg != "exit\0"); 41 42 Console.WriteLine("end."); 43 Console.ReadLine(); 44 45 ms.Close(); 46 stream.Close(); 47 tcp.Close(); 48 } 49}

C

1//サーバー側 2#include <stdio.h> 3#include <string.h> 4#include <sys/types.h> 5#include <sys/socket.h> 6#include <netinet/in.h> 7 8int main() 9{ 10 int sock0; 11 struct sockaddr_in addr; 12 struct sockaddr_in client; 13 int len; 14 int sock; 15 char buf[128]; 16 //char sendMsg[256] = "hello"; 17 int num = 0; 18 19 sock0 = socket(AF_INET, SOCK_STREAM, 0); 20 21 addr.sin_family = AF_INET; 22 addr.sin_port = htons(7777); 23 addr.sin_addr.s_addr = INADDR_ANY; 24 25 bind(sock0, (struct sockaddr *)&addr, sizeof(addr)); 26 27 //do { 28 listen(sock0, 5); 29 30 len = sizeof(client); 31 sock = accept(sock0, (struct sockaddr *)&client, &len); 32 33 do { 34 int recvMsglen; 35 recvMsglen = recv(sock, buf, sizeof(buf), 0); 36 printf("%s\n", buf); 37 38 char sendMsg[] = "hello"; 39 write(sock, sendMsg, sizeof(sendMsg)); 40 printf("sent :%s\n", sendMsg); 41 42 }while (strcmp(buf, "exit") != 0); 43 44 close(sock); 45 close(sock0); 46 47 return 0; 48} 49

投稿2019/08/23 08:06

編集2020/03/01 18:20
kaitorisenkou

総合スコア28

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問