ソケット通信(Linux)

目次

1.ソケット通信とは
 ソケットAPIを用いた異なるホスト間の通信のアプリケーションです。ソケットAPIはトランスポート層以下の機能を提供するプログラミングインターフェースでクライアント/サーバモデルの通信を行うために用いられます。

2.ソケットを用いたサーバ/クライアント間通信
 ソケットの種類として、TCPプロトコルを使うコネクション型のストリームソケットと、UDPプロトコルを使うコネクションレス型のデータグラムソケットがあります。
(1)ストリームソケットを使用したコネクション型の通信

(2)データグラムソケットを使用したコネクションレス型の通信

3.サンプルプログラム


3.1 コネクション型
 ストリームソケットを使用したコネクション型の通信のサンプルを作成します。
(1)サンプルの概要
 サーバはクライアントからの接続を待ち、クライアントがサーバに接続すると、サーバはクライアントからの送信電文をコンソールに表示する。サーバはクライアントに電文を送信して通信を終了する。クライアントはサーバからの電文をコンソールに表示して通信を終了する。

(2)ソースプログラム
 server.c

(※1)sockaddr_in構造体
struct sockaddr_in {
u_char sin_len;
u_char sin_family; (アドレスファミリ.今回はAF_INETで固定)
u_short sin_port; (ポート番号)
struct in_addr sin_addr; (IPアドレス)
char sin_zero[8];
};
ポート番号やIPアドレスはネットワークバイトオーダー (big endian) になっていないといけない。このため、整数をネットワークバイトオーダーに変換するhtons関数を用いる。

(※2)socket()
通信のための端点 (endpoint) を作成する
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
引数:
①domain
アドレスファミリーに指定できるプロトコル
AF_INET インターネットプロトコル
など
②type
通信方式 (semantics) を指定する。
定義されている型は現在以下
SOCK_STREAM:
順序性と信頼性があり、双方向の、接続された バイトストリーム (byte stream) を提供する。
帯域外 (out-of-band) データ転送メカニズムもサポートされる。
SOCK_DGRAM:
データグラム (コネクションレス、信頼性無し、固定最大長メッセージ) をサポートする。
など
③protocol
ソケットによって使用される固有のプロトコルを指定する。
与えられたプロトコルファミリーの種類ごとに一つのプロトコルのみをサポートする。その場合はprotocolに0を指定できる。
戻り値:ディスクリプタ
プログラムからファイルを操作する際、操作対象のファイルを識別・同定するために割り当てられる番号

(※3)bind()
ソケットが作成されたときは名前空間 (アドレスファミリー) に存在するがアドレスは割り当てられていない。bind() はファイルディスクリプターsockfdで参照されるソケットにaddrで指定されたアドレスを割り当てる。
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
返り値:成功:0  失敗:-1
引数:
①int sockfd
②sockaddr *addr
③socklen_t addrlen

(※4)listen()
ソケットを待ち状態にする
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int listen(int s, int backlog);
引数:
①s:
ソケットディスクリプタ
②backlog:
新規コネクションの最大数を指定する

(※5)accept()
クライアントからの接続を受け入れコネクションを確立する。この関数は、接続待ちソケットsocket宛ての保留状態の接続要求が入っているキューから先頭の接続要求を取り出し、接続済みソケットを新規に生成し、そのソケットを参照するための新しいファイルディスクリプターを返す。
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
引数:
①s
ソケットディスクリプタ
②addr
宛先の情報を戻すsockaddr構造体
③addrlen
sockaddr構造体の大きさを指定する

(※6)fdopen()
既存のファイルディスクリプターにストリームを結びつける。ストリームの mode (“r”, “r+”, “w”, “w+”, “a”, “a+” のいずれか) はファイルディスクリプターのモードと互換のものでなければならない。

(※7)fgets()
streamから最大でsize-1個の文字を読み込み、sが指すバッファーに格納する。読み込みは EOF または改行文字を読み込んだ後で停止する。読み込まれた改行文字はバッファーに格納される。終端のヌルバイト (‘\0’) が一つバッファーの中の最後の文字の後に書き込まれる。
(書式)
#include <stdio.h>
int fgetc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int getc(FILE *stream);
int getchar(void);
int ungetc(int c, FILE *stream);
char *fgets(char *s, int size, FILE *stream);

(※8)send(), sendto(), sendmsg()
ソケットが接続された (connected) 状態にある場合(送信相手が決まっている)にソケットへメッセージを送る 。
(書式)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
引数:
①int sockfd
②const void
③*buf
④size_t len
⑤int flags

(※9)close()
ファイルディスクリプターをクローズする
(書式)
#include <unistd.h>
int close(int fd);int close(int fd);

 client.c

(※1)hostent構造体
struct hostent {
char h_name; / ホストの正式名称 */
char *h_aliases; / 別名リスト / int h_addrtype; / ホストアドレスのタイプ (AF_INET6 など) / int h_length; / アドレスの長さ */
char *h_addr_list; / NULL で終わるアドレスのリスト */
};

(※2)gethostbyname()
ホスト名からIPアドレスを得る
(書式)
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);

(※3)connect()
ソケットの接続を行う
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);
ファイルディスクリプターsockfdが参照しているソケットをaddrで指定されたアドレスに接続する
戻り値:成功:0、失敗:-1

(※4)memcpy()
メモリー領域srcの先頭nバイトをメモリー領域 dest にコピーする。
(書式)
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);

(3)コンパイルと実行

サーバ側にはクライアントが送信したメッセージを表示し、クライアント側ではサーバが送信したメッセージが表示される。

3.2 コネクションレス型
 データグラムソケットを使用したコネクションレス型の通信のサンプルを作成します。
(1)サンプルの概要
 サーバはクライアントからの接続を待ち、クライアントがサーバに検索を要求すると、サーバは検索結果をクライアントに返す。クライアントは検索結果をコンソールに表示して通信を終了する。

(2)ソースプログラム
 server_cl.c

(※1)recvfrom()
ソケットからメッセージを受け取る
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

(※2)sendto()
ソケットへメッセージを送る
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

 client_cl.c

(3)コンパイルと実行

サーバ側にはクライアントから受信したkey値と検索結果を表示し、クライアント側ではサーバから受信した検索結果を表示する。

The end