0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

1年の振り返りAdvent Calendar 2024

Day 13

ネットワークプログラミングの基本的なシステムコールを理解する

Posted at

はじめに

RailsやNext.jsなどのアプリケーションフレームワークを使えば、簡単にネットワークを介したアプリケーションを作成することができますが、その背後にはOSが提供する基本的なネットワーク操作が存在しています。

アプリケーションはユーザランドに属しているため、カーネルランドに属しているTCP/UDP等のトランスポート層の機能を使うためにはシステムコールを呼び出す必要があります。

今回はTCPでネットワークプログラミングをする際に必要なシステムコールについて調べていきます。

socket()

まずはsocket()を呼び出すことによって、アプリケーション層とトランスポート層の間に口を作ります。

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

呼び出しの際の引数はそれぞれ以下を意味します。

  • domain
    • どのプロトコルファミリーで通信するかを指定します
    • ex
      • AF_INET: IPv4 Internet protocols
      • AF_INET6: IPv6 Internet protocols
      • AF_BLUETOOTH: Bluetooth low-level socket protocol
  • type
    • ソケットのタイプを指定します
    • ex
      • SOCK_STREAM: TCPコネクションで使われるsocketタイプ
      • SOCK_DGRAM: UDP通信で使われるsocketタイプ
  • protocol
    • ソケットが使用するプロトコルを指定します
    • ここで指定できるプロトコルは/etc/protocolsで管理されています

戻り値は、この呼び出しによって作成されたsocketを参照するfile descriptorです。

bind()

socket()で作成された、socketに対してアドレスを割り当てます。
ここで言うアドレスとはIPアドレスとport番号のことです。

#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd
    • bindするsocketのfile descirptorを指定します
    • socket()の戻り値がここに入ります
  • addr
    • 割り当てるアドレスを指定します
  • addrlen
    • 第2引数のconst struct sockaddr *addrで表される構造体のサイズを指定します

Serverの場合は特定のportに対してbindすることがほとんどですが、Clientからリクエストを送る際にもこのbind()は呼び出されています。

Clientからリクエストを送る際にはbind先を指定しないことがほとんどなので、kernelが暗黙に指定していますが、これらのportのことをephemeral portと呼びます。
ephemeral portとして使うことができる範囲は決められていて、/proc/sys/net/ipv4/ip_local_port_rangeで確認することができます。

listen()

listen()によって作成されたsocketがClient用ではなくてServer用であることを明示します。
この時点でServerにおけるClientからの接続要求の準備が整います。

#include <sys/socket.h>

int listen(int sockfd, int backlog);
  • sockfd
    • 対象のsocketのfile descriptorを指定します
  • backlog
    • queueに入れられる処理待ちのconnectionの最大サイズを指定します
    • このサイズに到達した場合、Clientはエラーを受け取ります

accept(), accept4()

accept()で、接続待ちコネクションが入っているqueueからコネクションを取り出して、そのコネクションに対応するためのread/write用のsocketを作成します。

#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *_Nullable restrict addr,
          socklen_t *_Nullable restrict addrlen);
  • sockfd
    • listening用のsocketのファイルディスクリプタを指定します
    • socket()の戻り値に等しいです
  • addr
    • 自socketのアドレスではなく、接続要求をしてきたClientのアドレスを指定します
  • addrlen
    • 第2引数の構造体のサイズを指定します

戻り値は、コネクションに対応するために作成したread/write用のsocketのfile descriptorです。

recv()

recv()で、対象のsocketからmessageを受け取ります。

#include <sys/socket.h>

ssize_t recv(int sockfd, void buf[.len], size_t len, int flags);
  • sockfd
    • Server: accpet()で作成したsocketのfile descriptを指定します
    • Client: socket()で作成したsocketのfile descriptを指定します
  • buf
    • messageを受け取るためのbufferのポインタを指定します
  • len
    • 第2引数のbufferの最大サイズを指定します
  • flags
    • 受信時の動作を指定するオプションフラグです

戻り値は受信したmessageのサイズになります。

send()

send()によって、接続しているsocketに対してmessageを送信します。

#include <sys/socket.h>

ssize_t send(int sockfd, const void buf[.len], size_t len, int flags);
  • sockfd
    • Server: accpet()で作成したsocketのfile descriptを指定します
    • Client: socket()で作成したsocketのfile descriptを指定します
  • buf
    • 送信したいデータが乗っているbufferのポインタを指定します
  • len
    • bufferのサイズを指定します
  • flags
    • 送信時の動作を指定するオプションフラグです

参考

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?