関数の仕様調べ - ryhara/Webserv GitHub Wiki

socket

ネットワークの通信に使用するソケットを作成し、ファイルディスクリプタを返す

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
  • ai_family AF_INET IPv4 専用のインターネットファミリ
  • ai_socktype SOCK_STREAM ストリーム型ソケット
  • ai_protocol AI_PASSIVE AI_PASSIVE ビットが hints 構造体の ai_flags メンバーの中に設定される場合、呼び出し元は、戻されたソケット・アドレス構造体を bind() の呼び出しに使用することを計画します。

使用例

_server_fd = socket(_res->ai_family, _res->ai_socktype, _res->ai_protocol);

getaddrinfo

ネットワークのアドレスとサービスを変換する

_hints は要求を指定するために使い、_res はその要求に基づいて得られた結果を受け取るために使います。 NULL : ローカルホストのアドレスを指定 SEVEER_PORT_STR : ポート番号の文字列を指定 _hints : アドレス情報の要求 _res : アドレス情報の格納先 使用例

ft_memset(&_hints, 0, sizeof(_hints));
_hints.ai_family = AF_INET;
_hints.ai_socktype = SOCK_STREAM;
_hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(NULL, SERVER_PORT_STR, &_hints, &_res) != 0)
    log_exit("getaddrinfo", __LINE__, __FILE__, errno);

参考

setsockopt

setsockopt()は、ソケットに対応するオプションを操作します。 ほとんどのソケットレベルのオプションは optval 用に int 引数を使用します。 setsockopt() の場合、ブール演算を有効にするためには引数は 0 でない必要があり、 オプションを無効にする場合は 0 である必要があります。 ソケット・レベルでオプションを操作するためには、SOCKET.H内で定義されているSOL_SOCKETをlevelパラメーターに設定しなければなりません。

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
SO_REUSEADDR | ローカルアドレスの再使用を有効にします

使用例

これを指定しないと、確かローカルアドレスの再使用に失敗してbindでエラーがでていた。

int option_value =1;
setsockopt(_server_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&option_value, sizeof(option_value))

bind

ソケットに名前をつけるような操作である。

bind() システムコールは、ローカルプロトコルアドレスをソケットに割り当てます。 ソケットは、socket()で作成される時にアドレスファミリ空間に存在しますが、 プロトコルアドレスは割り当てられていません。 bind() システムコールはソケットに addr を割り当てることを要求します。

ai_addr 戻されたソケット・アドレス構造体のアドレス。 このフィールドに戻される値は、AI_PASSIVE フラグに従って、 このソケット・タイプとともに connect() コールまたは bind() コールの引数として使用できます。

使用例

bind(_server_fd, _res->ai_addr, _res->ai_addrlen);
↓
freeaddrinfo(_res);

freeaddrinfo

freeaddrinfo() 関数は、 リンクリスト res に対して動的に割り当てられたメモリを解放する。

使用例

freeaddrinfo(_res);

listen

ソケット上の接続をlistenする

socket で最初にソケットが作成され、 着信接続を受け入れる意思および着信接続用の待ち行列限界が listen() で指定された後、接続が acceptで受け入れられます。 listen() システムコールは、タイプが SOCK_STREAM または SOCK_SEQPACKET のソケットにだけ適用されます。

引数は、延期中の接続の待ち行列を伸ばす際の最大長を定義します。 待ち行列が満杯のときに接続要求が到着すると、クライアントは ECONNREFUSED を示すエラーを受信する可能性があります。 TCP の場合は、接続は黙って落とされます。

使用例

listen(_server_fd, QUEUE_LENGTH) 

accept

ソケット上の接続を受け入れる。 引数 s は socketで作成され、 bindでアドレスにバインドされ、 listenも済ませて接続を待ち受けているソケットです。 accept()は、待ち行列上の最初の接続要求を取り出し、 新しいソケットを作成し、オリジナルのソケット s から O_NONBLOCK プロパティの状態を継承した そのソケットへ新しいファイル記述子を割り当てます。

引数 addr は結果の引数で、 通信レイヤに既知の接続エンティティのアドレスで埋められます。 addr 引数の正確な形式は通信が行われるドメインで決まります。

使用例

client_fd = accept(_server_fd, (struct sockaddr *)&client_addr, &client_addr_len);

EWOULDBLOCK ソケットが非ブロッキングとマークされ、さらに受け付けるべき接続要求が存在しません。 EAGAINとdefineは同じ errnoがこれの場合はエラーではない

recv

ソケットからメッセージを受け取るのに使用される。 最後の引数にflagを設定できる。

ソケットに受け取るメッセージが存在しなかった場合、 受信用のコールはメッセージが到着するまで待つ。 ただし、ソケットが非停止 (nonblocking) に設定されていた場合(fcntl) は -1 を返し、外部変数 errno に EAGAIN か EWOULDBLOCK を設定する。 これらの受信用のコールは、受信したデータのサイズが要求したサイズに 達するまで待つのではなく、何らかのデータを受信すると復帰する (受信されるデータの最大サイズは要求したサイズである)。

使用例

ssize_t n = recv(client_fd, _buffer, sizeof(_buffer) - 1, 0);

send

別のソケットにメッセージを送信する。最後の引数にフラグを設定できる。 非停止モードの場合にはエラー EAGAIN か EWOULDBLOCK で失敗する。 いつデータをさらに送信できるようになるかを知るために、 selectを使用することができる。

使用例

ssize_t send_n = send(client_fd, responseMessage.c_str(), responseMessage.size(), 0);

fcntl

ファイルディスクリプタの設定を行う

サブジェクト通りの使い方だと設定できないらしい pedagoに投げられている質問を例に正しいと思われる実装をした。

  • F_GETFL : 記述子の状態フラグを取得します
  • F_SETFL : fd に結び付けられた close-on-exec フラグを arg に設定します。 arg は 0 または前述の FD_CLOEXEC です。
  • O_NON_LOCK : readで読み取るべきデータがない場合、またはwrite操作がブロックするであろう場合に、読み取りまたは書き込み呼び出しはEGAINで-1を返す。  ブロックをせずにすぐにreturnをするノンブロッキング
  • FD_CLOEXEC : このフラグが 1 であるときに、プロセスが exec 関数呼び出し の 1 つを実行する場合、ファイル記述子はクローズされる。フラグが 0 の場合は、ファイルはオープンしたままである。

使用例

int flag = fcntl(fd, F_GETFL);
if (flag < 0) {
    log_exit("fcntl", __LINE__, __FILE__, errno);
} else {
    if (fcntl(fd, F_SETFL, flag | O_NONBLOCK) < 0) {
        log_exit("fcntl/F_SETFL", __LINE__, __FILE__, errno);
    }
    if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
        log_exit("fcntl/F_SETFD", __LINE__, __FILE__, errno);
    }
}

poll

入出力の多重化

poll() システムコールはファイル記述子の集合を調査して、 それらのいずれかで入出力の準備ができているか否かを調べます。 fds 引数は <poll.h> で定義された pollfd 配列を指すポインタ (後述) です。 nfds 引数は fds 配列のサイズを決定します。

#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);

struct pollfd {
    int    fd;       /* 調べるファイルディスクリプタ fdが-1ならばreventsはクリア(0に設定)されpollfdはちぇっくされない*/
    short  events;   /* 検索するイベント */
    short  revents;  /* 返された、発生したイベント */
};
  • POLLIN : 高優先データ以外のデータはブロックせずに読取りできます。
  • POLLERR : デバイスまたはソケット上に例外状態が起きました。 このフラグは、 events ビットマスクに存在していなくても必ずチェックされます。

timeout が 0 でも INFTIM (-1) でもない場合、この値はいずれかのファイル記述子が 準備完了になるのを待機する最大間隔 (ミリ秒単位) です。 timeout が INFTIM (-1) の場合、 poll() は無期限にブロックします。 timeout が 0 の場合 poll() はブロックせずに戻ります。

select

selectはアドレスで渡されたfd集合を調べ、それらのfdのいくつが準備完了であるか調べる。

最初のnfds個のfdが各集合でチェックされる。戻り時に記述し集合を、要求された操作の準備ができているfdで構成される集合に置き換える。返り値は準備出来たfdの総数を返す

#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

これらのマクロは fdの値が0未満、またはFD_SETSIZE = 1024以上である場合は未定義

  • FD_ZERO(&fdset); fd集合を空集合で初期化
  • FD_SET(fd, &fdset); fdをfdsetに加える
  • FD_CLR(fd, &fdset); fdをfdsetから削除
  • FD_ISSET(fd, &fdset); fdがfdsetのメンバである場合は非ゼロ、それ以外は0を返す。

timoutがNULL出ない場合、selectの完了を待つ最大間隔を指定。NULLの場合selectは無期限にブロック Nob-blockingファイルディスクリプタの場合、selectは直ちに戻る

使用例

ノンブロッキングI/O

ノンブロッキングI/Oはfdの準備が完了していない場合即座にエラーを返す。errno=EAGAIN

ノンブロッキングI/Oは即座にエラーを返し、ブロック状態にさせない。非同期I/Oは処理が終了するまで待機して、終了したら通知を返す。

⚠️ **GitHub.com Fallback** ⚠️