環境
サーバ
- Ubuntu 18.04 LTS on Virtual Box
- GCC 7.4.0
クライアント
- Windows 10
- Ruby 2.6.3
サーバとクライアントはホストオンリーアダプタで接続。
質問
勉強のため、サーバアプリケーションをC言語で作成しています。
listen
関数で指定した backlog
を大きく超える接続があった場合の挙動を調べていたのですが、一部の接続はサーバ側にacceptされず、クライアント側にもエラーが発生しないデッドロックのような状態になりました。
man listen
によると、このような状況の場合、クライアント側に ECONNREFUSED
が発生すると思うのですが、例外は発生していないように見えます。
次の2点についてご意見を頂きたいです。
- 何が起きているのでしょうか?パケットが破棄された?
backlog
の値を大きくする、クライアント側がタイムアウト処理を実装する以外に解決策はありますか?
該当のソースコード
実行手順
サーバ側(ubuntu)
gcc code.c
./a.out
クライアント側(windows)
ruby ./code.rb
c
1#include <stdio.h> 2#include <stdlib.h> 3#include <sys/types.h> 4#include <sys/socket.h> 5#include <arpa/inet.h> 6#include <signal.h> 7#include <unistd.h> 8 9#include <string.h> 10 11const uint16_t port = 8880; 12 13int listen_sockfd = -1; 14 15void handle_int(int sgn) { 16 close(listen_sockfd); 17 printf("close socket\n"); 18 exit(0); 19} 20 21 22int main(){ 23 struct sockaddr_in server_sockaddr; 24 struct sockaddr_in client_sockaddr; 25 int res; 26 27 listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 28 if (listen_sockfd < 0) 29 perror("failed: socket"), 30 abort(); 31 32 signal(SIGINT, handle_int); 33 34 35 int optval = 1; 36 setsockopt(listen_sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)); 37 38 memset(&server_sockaddr, 0, sizeof(server_sockaddr)); 39 server_sockaddr.sin_family = AF_INET; 40 server_sockaddr.sin_port = htons(port); 41 server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 42 43 res = bind( 44 listen_sockfd, 45 (struct sockaddr *) &server_sockaddr, 46 sizeof(server_sockaddr) 47 ); 48 if (res < 0) 49 perror("failed: bind"), 50 abort(); 51 52 res = listen(listen_sockfd, 2); 53 if (res < 0) 54 perror("failed: listen"), 55 abort(); 56 57 for (int cnt = 0; ; ++cnt) { 58 socklen_t client_sockaddr_size; 59 int client_sockfd = accept( 60 listen_sockfd, 61 (struct sockaddr *) &client_sockaddr, 62 &client_sockaddr_size 63 ); 64 if (client_sockfd < 0) { 65 perror("failed: accept"); 66 abort(); 67 } 68 printf("accept connection: (%d) port=%d, addr=%s\n", 69 cnt, 70 htons(client_sockaddr.sin_port), 71 inet_ntoa(client_sockaddr.sin_addr)); 72 73 dprintf(client_sockfd, "Hello! (%d)\n", cnt); 74 fsync(client_sockfd); 75 shutdown(client_sockfd, SHUT_WR); 76 77 close(client_sockfd); 78 } 79 80 close(listen_sockfd); 81 82 return 0; 83}
ruby
1require 'socket' 220.times.map do |i| 3 Thread.start(i) do |i| 4 begin 5 TCPSocket.open("192.168.###.###", 8880) do |io| 6 puts "%3d: connect fd=%d"%[i, io.fileno]; STDOUT.flush 7 io.gets 8 puts "%3d: success fd=%d"%[i, io.fileno]; STDOUT.flush 9 end 10 rescue 11 puts "%3d: error"%[i]; STDOUT.flush 12 end 13 end 14end.each do |th| 15 th.join 16end
実行結果
server
accept connection: (0) port=60502, addr=192.168.56.1 accept connection: (1) port=60503, addr=192.168.56.1 accept connection: (2) port=60504, addr=192.168.56.1 accept connection: (3) port=60515, addr=192.168.56.1 accept connection: (4) port=60518, addr=192.168.56.1 accept connection: (5) port=60519, addr=192.168.56.1 accept connection: (6) port=60520, addr=192.168.56.1 accept connection: (7) port=60521, addr=192.168.56.1 accept connection: (8) port=60507, addr=192.168.56.1 accept connection: (9) port=60514, addr=192.168.56.1 accept connection: (10) port=60511, addr=192.168.56.1 accept connection: (11) port=60508, addr=192.168.56.1 accept connection: (12) port=60512, addr=192.168.56.1 accept connection: (13) port=60516, addr=192.168.56.1 accept connection: (14) port=60510, addr=192.168.56.1 accept connection: (15) port=60513, addr=192.168.56.1
client
0: connect fd=3 6: connect fd=4 11: connect fd=5 13: connect fd=6 4: connect fd=7 5: connect fd=8 2: connect fd=16 16: connect fd=19 17: connect fd=20 18: connect fd=21 19: connect fd=22 0: success fd=3 6: success fd=4 11: success fd=5 2: success fd=16 16: success fd=19 17: success fd=20 18: success fd=21 19: success fd=22 5: success fd=8 14: connect fd=15 15: connect fd=9 8: connect fd=12 14: success fd=15 10: connect fd=13 15: success fd=9 3: connect fd=17 1: connect fd=11 12: connect fd=14 7: connect fd=18 8: success fd=12 9: connect fd=10 10: success fd=13 3: success fd=17 1: success fd=11 12: success fd=14
connectの数とsuccessの数が一致しておらず、一部のソケットが待ち状態になったままである。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。