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

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

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

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

TCP

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

Q&A

解決済

1回答

3244閲覧

listen 関数で指定した backlog を大きく超える接続について

maai

総合スコア463

C

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

TCP

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

0グッド

2クリップ

投稿2019/06/16 02:51

編集2019/06/22 06:14

環境

サーバ

  • 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)

  1. gcc code.c
  2. ./a.out

クライアント側(windows)

  1. 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の数が一致しておらず、一部のソケットが待ち状態になったままである。

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

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

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

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

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

guest

回答1

0

ベストアンサー

socklen_t client_sockaddr_size; int client_sockfd = accept( listen_sockfd, (struct sockaddr *) &client_sockaddr, &client_sockaddr_size );

変数client_sockaddr_sizeが未初期化です。accept関数呼び出し前にclient_sockaddrバッファサイズを設定する必要があります。

socklen_t client_sockaddr_size = sizeof(client_sockaddr);

投稿2019/07/02 04:04

yohhoy

総合スコア6191

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問