📦

Socket通信 (Windows C++/C#)

2022/08/24に公開

やりたかったこと

他のアプリとのプロセス間通信をしたくて、socket通信を使ってみた。
送信側がC++、受信側がC#です。

やり方

やり方はいろんなサイトに紹介されてます。
C#での受信の一例は下記のとおり。

IPAddress host1 = IPAddress.Parse("127.0.0.1");
int port1 = 12345;
IPEndPoint ipe1 = new IPEndPoint(host1, port1);
TcpListener server = null;
byte[] buf = new byte[SentDataLength];
server = new TcpListener(ipe1);
server.Start();
int count = 0;
while(count < 10)
{
    using (var client = server.AcceptTcpClient()){
        using (var stream = client.GetStream()){
            while ((stream.Read(buf, 0, buf.Length)) == buf.Length){
	        // 受信したものへの処理
	    }
	}
    }
    count++;
}
server.Stop();

C++での送信の一例が下記のとおり。

WSADATA wsa_data;
sockaddr_in addr;
WSAStartup(MAKEWORD(2, 0), &wsa_data)
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
int port1 = 12345;
std::string address1 = "127.0.0.1";
addr.sin_port = htons(port1);
inet_pton(addr.sin_family, address1.c_str(), &addr.sin_addr.s_addr);
int socket1 = socket(AF_INET, SOCK_STREAM, 0);
connect(socket1, (struct sockaddr*)&addr, sizeof(addr));
char* cdata = new char[send_length];
// cdataの値を設定するなにかしらの処理(省略)
send(socket1, cdata, send_length, 0);
closesocket(socket1);
delete [] cdata;
// 終了処理
WSACleanup();

つまったところ

socket関連の謎のエラー

エラー内容:

'sockaddr': 'struct'型の再定義
構文エラー:'}'が'定数'の前にありません。
・・・

↑まったく心あたりのない大量のエラー。

対処方法:
winsock2.hのインクルードがwindows.hより前になるように修正
windows.hとのインクルード順のエラーは本当に多い。。。

ライブラリ追加忘れ

エラー内容:

外部シンボル__imp_htonsは未解決です
外部シンボル__imp_connectは未解決です
外部シンボル__imp_socketは未解決です
外部シンボル__imp_sendは未解決です
・・・

対処方法:
ws2_32.lib を追加の依存ファイルに追加

char配列ではなくshort配列を受け渡ししたい

いろんなサイトに紹介してあるのは、スタンダードなchar配列の送受信です。
簡単なIFの関数がないのか探したのですが見つからず。今回short型を送るにあたり、どうやら自分で素直にchar型(byte型)に変換するしかなさそう。
下記は送る側のC++コード。

char* cdata = new char[send_length];
int num = 0;
for(int i = 0; i < N; i++)
{
	short sdata = (short)data[i];
	cdata[num] = (char)(sdata & 0xff);
	cdata[num + 1] = (char)((sdata >> 8) & 0xff);
	num += 2;
}
// 送る
send(dst_socket, cdata, send_length, 0);

下記が受け取る側のC#コード。

while ((stream.Read(buf, 0, buf.Length) == buf.Length)
{
    for(int i = 0; i < N; i++)
    {
	short d1 = buf[i * 2];
	short d2 = buf[i * 2 + 1];
	short sData = (short)(d1 | (short)(d2 << 8));
	data[i] = sData;
    }
}

動かない問題 server.AcceptTcpClient()

server = new TcpListener(ipe1);
server.Start();
while(!stopApp)
{
    using (var client = server.AcceptTcpClient()){
        using (var stream = client.GetStream()){
            while ((stream.Read(buf, 0, buf.Length)) == buf.Length){
	        // 受信したものへの処理
	    }
	}
    }
    await Task.Delay(1000);
}
server.Stop();

上記のような受信側のコードを書いたとき、client側が送信のための接続をしてくれない場合、接続が確立せず、ずっとserver.AcceptTcpClient()で待ち続けるということが起こりました。
当初の予定ではエラーが起きてcatchのとこでTimeoutのようなエラーを処理するようにすればいいかと思っていたのですが、エラーなしで、待ち続ける仕様でした。
というわけで、調べると、タイムアウトを設定できるらしい。上のほうの2行に下記を追加。1秒だけ待ちます。

server = new TcpListener(ipe1);
Socket serverSocket = server.Server;
LingerOption lingerOption = new LingerOption(true, 1);  // 1秒待つ
serverSocket.SetSocketOption(
    SocketOptionLevel.Socket, SocketOptionName.Linger, lingerOption);
server.Start();

これで、動かない問題は解消しました。

下記が参考にしたサイトです。
https://docs.microsoft.com/ja-jp/dotnet/api/system.net.sockets.tcplistener.server?view=net-6.0

Discussion