IoT全盛期(?)で低レイヤーから通信をすることが多くなったと思います。
ArduinoやRaspberryPiなどは先人達が作ってくれたありがたいライブラリが使えるので特に意識することなく使用していましたが、 その裏で何が起こっているのか、通信の概念の一例としてソケット通信を勉強しようと思いました。
ソケット通信とは
ソケット通信とは、ソケットというファイルを作成し、通常のファイルと同じようにソケットに対して読み書きすることで相手と通信できるような仕組みのようです。
ソケット通信の流れ
ソケット通信のおおまかな流れは下記の通りです。
サーバ(ホスト側)
1. socket()でソケットを作成、ソケットディスクリプタを返す
2. bind()でソケットに自身のIPアドレスやポート番号を割り当てる
3. listen()でクライアントからの接続要求を待ち受ける
4. accept()でクライアントからの接続要求を受け付ける
5. send(), recv()でクライアントと通信する
6. close()でクライアントとの接続を終了する
クライアント
1. socket()でソケットを作成、ソケットディスクリプタを返す
2. connect()でサーバへ接続する
3. send(), recv()でサーバと通信する
4. close()でサーバとの接続を終了する
【ソケットの作成】
int socket(int protocolFamily, int type, int protocol)
TCP,UDPなどのソケットを作成する。
ヘッダファイル
#include<sys/types.h> #include<sys/socket.h>
引数
引数名 | 説明 |
---|---|
protocolFamily | TCP/IPのソケットの場合はPF_INET |
type | ソケットのタイプ(SOCK_STREAM, SOCK_DGRAM) |
protocol | ソケットのプロトコル(IPPROTO_TCP, IPPROTO_UDP) |
戻り値
成功 | 失敗 |
---|---|
作成したソケットのディスクリプタを返す。 | -1 |
サンプルコード
int socket = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)
【ソケットの割当】
int bind(int socket, struct sockaddr* localAddress, unsigned int addressLength)
IPアドレスとポートをソケットに割り当てる。
ヘッダファイル
#include<sys/types.h> #include<sys/socket.h>
引数
引数名 | 説明 |
---|---|
socket | ソケットディスクリプタ |
localAddress | IPアドレスを格納したsockaddr構造体 |
addressLength | localAddressのサイズ(sizeof(localAddress)) |
戻り値
成功 | 失敗 |
---|---|
0 | -1 |
サンプルコード
hostAddrLen = (unsigned int)sizeof(hostAddr); if(bind(hostSocket,(struct sockaddr*)&hostAddr,hostAddrLen) < 0){ fprintf(stderr,"bind() failed\n"); }
【ソケットを接続しにいく】
int connect(int socket, struct sockaddr* hostAddress, int addressLength)
クライアントで使用する。 クライアント側のソケットと、ホスト側のソケットとの間に、コネクションを確立する。
ヘッダファイル
#include<sys/types.h> #include<sys/socket.h>
引数
引数名 | 説明 |
---|---|
socket | ソケットディスクリプタ |
hostAddress | 宛先IPアドレスを格納したsockaddr構造体 |
addressLength | hostAddressのサイズ(sizeof(hostAddress)) |
戻り値
成功 | 失敗 |
---|---|
0 | -1 |
サンプルコード
hostAddrLen = sizeof(hostAddr); if(connect(clientSocket,(struct sockaddr*)&hostAddr,hostAddrLen) < 0){ fprintf(stderr,"connect() failed\n"); }
【ソケットの接続を待ち受ける】
int listen(int socket, int backlog)
ホスト側のソケットを接続待ち(Listen)状態にする。
ヘッダファイル
#include<sys/types.h> #include<sys/socket.h>
引数
引数名 | 説明 |
---|---|
socket | ソケットディスクリプタ |
backlog | 待ち受けるソケットの最大数 |
戻り値
成功 | 失敗 |
---|---|
0 | -1 |
サンプルコード
if(listen(hostSocket, 5) < 0){ fprintf(stderr,"listen() failed\n"); }
【ソケットへの接続要求を受け付ける】
int accept(int socket, struct sockaddr clientAddress, int addressLength)
クライアントからのソケットへの接続要求を受け付けます。 TCPハンドシェイクが正常に終了すると、クライアントのソケットが返される。
ヘッダファイル
#include<sys/types.h> #include<sys/socket.h>
引数
引数名 | 説明 |
---|---|
socket | ソケットディスクリプタ |
clientAddress | クライアントのIPやPortを格納する構造体へのポインタ |
addressLength | clientAddressのサイズへのポインタ |
戻り値
成功 | 失敗 |
---|---|
クライアントのソケットディスクリプタ | -1 |
サンプルコード
unsigned int clientAddrLen = (unsigned int)sizeof(clientAddr); int clientSocket = accept(hostSocket,(struct sockaddr*)&clientAddr,&clientAddrLen);