Linux Network - yusukew62/docs GitHub Wiki

Linux Network

Socket

File Descriptor

sample code

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>

int main()
{
  int sock[10000];
  int i,j;

  scanf("%d", &j);

  for(i=1; i<1025; i++){
    sock[i] = socket(AF_INET, SOCK_STREAM, 0);
    printf("%d: %d\n", i, sock[i]);
    if(sock[i]<0){
      printf("%d:%s\n", errno, strerror(errno));
    }
  }
  scanf("%d", &j);

  return 0;
}

pidを確認

# ps -ef| grep -v grep | grep a.out
root      21364   1428  0 09:47 pts/0    00:00:00 ./a.out

pidのprocへ移動

# cd /proc/21364/fd

上記のワンライナー

# aout=$(ps -ef| grep -v grep | grep a.out| awk '{print $2}'); cd /proc/${aout}

ソケット作成前のFDは0-2の3つ(0:stdin, 1:stdout, 2:stderr)

# ll
合計 0
lrwx------. 1 root root 64  3月  4 09:49 2018 0 -> /dev/pts/0
lrwx------. 1 root root 64  3月  4 09:49 2018 1 -> /dev/pts/0
lrwx------. 1 root root 64  3月  4 09:47 2018 2 -> /dev/pts/0

ソケットはFD番号3から作成され、標準では1023までのため1024を作成する際にエラーが出る

# ./a.out
1: 3
2: 4
3: 5
(省略)
1020: 1022
1021: 1023
1022: -1
24:Too many open files
1023: -1
24:Too many open files

作成されたソケットのファイルを確認

# ll | grep 1023
lrwx------. 1 root root 64  3月  4 09:49 2018 1023 -> socket:[64416]

FDの1023はシンボリックリンク

# stat 1023
  File: `1023' -> `socket:[64416]'
  Size: 64              Blocks: 0          IO Block: 1024   シンボリックリンク
Device: 3h/3d   Inode: 65446       Links: 1
Access: (0700/lrwx------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-03-04 09:49:28.151503039 +0900
Modify: 2018-03-04 09:49:28.134503015 +0900
Change: 2018-03-04 09:49:28.134503015 +0900

実体はソケットファイル

# stat -L 1023
  File: `1023'
  Size: 0               Blocks: 0          IO Block: 4096   ソケット
Device: 6h/6d   Inode: 64416       Links: 1
Access: (0777/srwxrwxrwx)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 1970-01-01 09:00:00.000000000 +0900
Modify: 1970-01-01 09:00:00.000000000 +0900
Change: 1970-01-01 09:00:00.000000000 +0900

straceでシステムコールを確認すると下記のようになっている

socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 1022
write(1, "1020: 1022\n", 111020: 1022
)            = 11
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 1023
write(1, "1021: 1023\n", 111021: 1023
)            = 11
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = -1 EMFILE (Too many open files)
write(1, "1022: -1\n", 91022: -1
)               = 9
write(1, "24:Too many open files\n", 2324:Too many open files
) = 23
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = -1 EMFILE (Too many open files)
write(1, "1023: -1\n", 91023: -1
)               = 9

Server/Client

Server

sample code

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>

int main()
{
  int sock0;
  int len;
  int sock;

  struct sockaddr_in server;
  struct sockaddr_in client;

  sock0=socket(AF_INET, SOCK_STREAM, 0);
  if(sock0 < 0){
    perror("socket");
  }

  server.sin_family = AF_INET;
  server.sin_port = htons(12345);
  server.sin_addr.s_addr = INADDR_ANY;
  if(bind(sock0, (struct sockaddr *) &server, sizeof(server))!=0) {
    perror("bind");
  }

  if(listen(sock0, 5)!=0) {
    perror("listen");
  }

  len = sizeof(client);
  sock = accept(sock0, (struct sockaddr *) &client, &len);
  if(sock < 0) {
    perror("socket");
  }

  write(sock, "Hello", 5);

  close(sock);

  close(sock0);

  return 0;
}

Client

sample code

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>

int main()
{
  int sock;
  struct sockaddr_in client;
  char buf[32];
  int n;

  sock=socket(AF_INET, SOCK_STREAM, 0);
  if(sock < 0) {
    perror("socket");
  }

  client.sin_family = AF_INET;
  client.sin_port = htons(12345);
  client.sin_addr.s_addr = inet_addr("127.0.0.1");

  if(connect(sock, (struct sockaddr *) &client, sizeof(client))<0) {
    perror("connect");
  }

  memset(buf, 0, sizeof(buf));
  n = read(sock, buf, sizeof(buf));

  printf("%d, %s\n", n, buf);

  close(sock);

  return 0;
}

動作確認

クライアント側のシステムコール

# strace ./cl
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
read(3, "Hello", 32)                    = 5
close(3)                                = 0

サーバ側のシステムコール

# strace ./sv
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 5)                            = 0
accept(3, {sa_family=AF_INET, sin_port=htons(37286), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4
write(4, "Hello", 5)                    = 5
close(4)                                = 0
close(3)                                = 0

クライアント接続前はLISTEN状態

tcp        0      0 0.0.0.0:12345               0.0.0.0:*                   LISTEN      0          154500
tcp        0      0 127.0.0.1:37290             127.0.0.1:12345             ESTABLISHED 0          154584
tcp        0      0 127.0.0.1:12345             127.0.0.1:37290             ESTABLISHED 0          154501

サーバ側FD

lrwx------. 1 root root 64  3月  4 14:22 2018 3 -> socket:[154500]
lrwx------. 1 root root 64  3月  4 14:22 2018 4 -> socket:[154501]

クライアント接続後はTIME_WAIT状態

tcp        0      0 127.0.0.1:37290             127.0.0.1:12345             TIME_WAIT   0          0

備考

backlogは保留中の接続のキューの最大長を指定する

int listen(int sockfd, int backlog);

不完全なソケットのキューの最大長は /proc/sys/net/ipv4/tcp_max_syn_backlog を用いて設定できる

# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
512

backlog 引き数が /proc/sys/net/core/somaxconn の値よりも大きければ、 backlog の値は暗黙のうちにこの値に切り詰められる

# cat /proc/sys/net/core/somaxconn
128

ライブラリ

インターネットプロトコル(INET)

以下のヘッダファイルに定義されている

/usr/include/linux/in.h

sockaddr_in構造体

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__   16              /* sizeof(struct sockaddr)      */
struct sockaddr_in {
  sa_family_t           sin_family;     /* Address family               */
  __be16                sin_port;       /* Port number                  */
  struct in_addr        sin_addr;       /* Internet address             */

  /* Pad to size of `struct sockaddr'. */
  unsigned char         __pad[__SOCK_SIZE__ - sizeof(short int) -
                        sizeof(unsigned short int) - sizeof(struct in_addr)];
};

in_addr構造体

/* Internet address. */
struct in_addr {
        __be32  s_addr;
};

INADDR_ANY

/* Address to accept any incoming messages. */
#define INADDR_ANY              ((unsigned long int) 0x00000000)

ソケット

listen()が含まれているヘッダファイル

/usr/include/sys/socket.h

listen()の中身

extern int listen (int __fd, int __n) __THROW;

/* Await a connection on socket FD.
   When a connection arrives, open a new socket to communicate with it,
   set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting
   peer and *ADDR_LEN to the address's actual length, and return the
   new socket's descriptor, or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
⚠️ **GitHub.com Fallback** ⚠️