t49's blog

電子工作初心者のブログ

【TCP/IP】ソケットプログラミング

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