- キット各種完売。
- msBerryDAC改造情報
2007/05/23(水)UDPをTCPにクローンするプログラム
UDPを受信してTCPにクローンするプログラム(複数クライアント対応)。本当はマルチスレッドで書いてあげるべきなのですが、そこまですると(実現する処理に対して)高級するぎる気がしたのでポーリングにしました。
というのも、TCPの通信エラーとかが出たときに(ブロックしないはずの)ソケットへの書き込み(データの送信)がバッファ一杯になるとストールして(止まって)しまうんですよね。もちろんソケットや書き込み時の関数はノンブロッキングに設定しておいても、です。C ではまだ確認していませんが、Perlで書いたときはそうでした。
TCP/IPはただ通信するだけなら簡単ですが、この手のエラー処理を考えはじめると非常に頭を使います。
プログラム
メイン部のみ。適当に main ルーチンを書けば Windows でも Unix系 でも動きます。
int max_connections=100; char buffer[0x10000]; int connection_pool[max_connections]; int accept_client(int listen_sock) { int sock, len; struct sockaddr_in sin; // accept len = sizeof(sin); sock = accept(listen_sock, (struct sockaddr *)&sin, &len); if (sock<0) error_return("client accept error"); // 接続者情報 printf("[%02d] Connection from %s\n", sock, inet_ntoa(sin.sin_addr)); // ソケットの設定 set_non_blocking(sock); return sock; } ////////////////////////////////////////////////////////////////////////////// // server main ////////////////////////////////////////////////////////////////////////////// int udp2tcp_server_main(int udp_sock, int tcp_sock) { int i; char buf[1024]; // select用の設定 fd_set fdbits; fd_set fdbits_org; FD_ZERO(&fdbits_org); FD_SET(udp_sock, &fdbits_org); FD_SET(tcp_sock, &fdbits_org); while(1) { // Select memcpy(&fdbits, &fdbits_org, sizeof(fdbits_org)); select(FD_SETSIZE, &fdbits, NULL, NULL, NULL); // New connection from TCP if ( FD_ISSET(tcp_sock, &fdbits) ) { int newsock = accept_client(tcp_sock); if (newsock>0) { // search for free connection pool point for(i=0; i<max_connections; i++) if (!connection_pool[i]) break; if (i<max_connections) { // ソケット番号をセーブ FD_SET(newsock, &fdbits_org); connection_pool[i] = newsock; dbg("[%02d] save to connection_pool[%d]\n", newsock, i); } else { // 接続を切る printf("Connections max\n"); close(newsock); } } } // Recieved from UDP int size = 0; if ( FD_ISSET(udp_sock, &fdbits) ) { size = recv(udp_sock, buffer, BUF_SIZE, MSG_DONTWAIT); if (size<0) error_exit2("UDP recv error(%d)", size); dbg("[%02d] Recieved UDP %d bytes\n", udp_sock, size); } // TCP接続クライアントにデータを送信 for(i=0; i<max_connections; i++) { int sock = connection_pool[i]; if (!sock) continue; // socket からデータ受信 if ( FD_ISSET(sock, &fdbits) ) { int s = recv(sock, buf, 1024, MSG_DONTWAIT); if (s<0) { FD_CLR(sock, &fdbits_org); connection_pool[i] = 0; dbg("[%02d] clear to connection_pool[%d]\n", sock, i); printf("[%02d] Connection close\n", sock); close(sock); continue; } } // データ送信 if (size>0) { //int s = write(sock, buf, size); int s = send(sock, buf, size, MSG_DONTWAIT); dbg("[%02d] write TCP %d bytes\n", sock, s); } } } }