ソケットプログラミング

 

1. 定義

ソケット: Socket。TCP/IP アプリケーションを作成するための抽象化されたインターフェース。

 

2. ソケットの使用例

インターネット電話やインターネット放送を含むすべての TCP/IP アプリケーション。

 

3. ソケット関数

まずは sockaddr_in 構造体に適切なパラメータ設定を行い、以下のソケット関数を実行する。

共通
socket ソケットの作成
bind ソケットとポートの結合
setsockopt ソケットオプションの設定
close (Linux), closesocket (Win) ソケットの終了
htonl ホスト・バイトオーダーをネットワーク・バイトオーダーに変換 (4バイト)
htons ホスト・バイトオーダーをネットワーク・バイトオーダーに変換 (2バイト)
ntohl ネットワーク・バイトオーダーをホスト・バイトオーダーに変換 (4バイト)
ntohs ネットワーク・バイトオーダーをホスト・バイトオーダーに変換 (2バイト)
inet_addr 文字列をIPアドレスに変換
inet_ntoa IPアドレスを文字列に変換
TCP
listen 接続キューの作成 (サーバ側)
accept 接続受入れ (サーバ側)
connect サーバへの接続 (クライアント側)
send パケット送信
recv パケット受信
UDP
sendto  パケット送信
recvfrom パケット受信

 

4. 簡単なTCP/IPプログラム

以下は、クライアントがサーバに接続し、1秒ごとにメッセージを送信するプログラムである (ただし、エラー処理をまったく行っていない悪いプログラムである)。まずサーバ側を起動し、別のマシンでクライアント側を起動する。Windows 用と Linux 用のプログラムの違いはほとんどなく、

インクルードするヘッダファイル
Windows で TCP/IP を有効にするための WSA 関数
ソケット終了のための closesocket (Windows) と close (Linux) の違い

しかない。言いかえれば、ソケットは OS に依存しない関数群である。

 

Windows版: リンクオプションに ws2_32.lib を加える。

TCP サーバ側

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#define BUFFER_SIZE 256

int
main() {
  /* ポート番号、ソケット */
  unsigned short port = 9876;
  int srcSocket;  // 自分
  int dstSocket;  // 相手

  /* sockaddr_in 構造体 */
  struct sockaddr_in srcAddr;
  struct sockaddr_in dstAddr;
  int dstAddrSize = sizeof(dstAddr);

  /* 各種パラメータ */
  int numrcv;
  char buffer[BUFFER_SIZE];

  /************************************************************/
  /* Windows 独自の設定 */
  WSADATA data;
  WSAStartup(MAKEWORD(2,0), &data); 

  /* sockaddr_in 構造体のセット */
  memset(&srcAddr, 0, sizeof(srcAddr));
  srcAddr.sin_port = htons(port);
  srcAddr.sin_family = AF_INET;
  srcAddr.sin_addr.s_addr = htonl(INADDR_ANY);

  /* ソケットの生成 */
  srcSocket = socket(AF_INET, SOCK_STREAM, 0);

  /* ソケットのバインド */
  bind(srcSocket, (struct sockaddr *) &srcAddr, sizeof(srcAddr));

  /* 接続の許可 */
  listen(srcSocket, 1);

  /* 接続の受付け */
  printf("Waiting for connection ...\n");
  dstSocket = accept(srcSocket, (struct sockaddr *) &dstAddr, &dstAddrSize);
  printf("Connected from %s\n", inet_ntoa(dstAddr.sin_addr));

  /* パケット受信 */
  while(1) {
    numrcv = recv(dstSocket, buffer, BUFFER_SIZE, 0); 
    if(numrcv == 0 || numrcv == -1) {
      status = closesocket(dstSocket); break;
    }
    printf("received: %s\n", buffer);
  }

  /* Windows 独自の設定 */
  WSACleanup();
}
 

TCP クライアント側

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

int
main() {
  /* IP アドレス、ポート番号、ソケット */
  char destination[80];
  unsigned short port = 9876;
  int dstSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in dstAddr;

  /* 各種パラメータ */
  int status;
  int numsnt;
  char *toSendText = "This is a test";

  /************************************************************/

  /* Windows 独自の設定 */
  WSADATA data;
  WSAStartup(MAKEWORD(2,0), &data));

  /* 相手先アドレスの入力 */
  printf("Connect to ? : (name or IP address) ");
  scanf("%s", destination);

  /* sockaddr_in 構造体のセット */
  memset(&dstAddr, 0, sizeof(dstAddr));
  dstAddr.sin_port = htons(port);
  dstAddr.sin_family = AF_INET;
  stAddr.sin_addr.s_addr = inet_addr(destination);
 
  /* ソケット生成 */
  dstSocket = socket(AF_INET, SOCK_STREAM, 0);

  /* 接続 */
  printf("Trying to connect to %s: \n", destination);
  connect(dstSocket, (struct sockaddr *) &dstAddr, sizeof(dstAddr));

  /* パケット送出 */
  for(i=0; i<10; i++) {
    printf("sending...\n");
    send(dstSocket, toSendText, strlen(toSendText)+1, 0);
    Sleep(1000);
  }

  /* Windows 独自の設定 */
  closesocket(dstSocket);
  WSACleanup();
}

UDP サーバ側

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#define BUFFER_SIZE 256

int
main() {
  /* ポート番号、ソケット */
  int port = 9876;
  int recvSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in recvSockAddr;

  /* 各種パラメータ */
  int status;
  int numrcv;
  char buffer[BUFFER_SIZE];
  unsigned long on = 1;

  /************************************************************/
  /* Windows 独自の設定 */
  WSADATA data;
  WSAStartup(MAKEWORD(2,0), &data));

  /* sockaddr_in 構造体のセット */
  memset(&recvSockAddr, 0, sizeof(recvSockAddr));
  recvSockAddr.sin_port = htons(port);
  recvSockAddr.sin_family = AF_INET;
  recvSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

  /* ソケット生成 */
  recvSocket = socket(AF_INET, SOCK_DGRAM, 0);
 
  /* バインド */
  status = bind(recvSocket, (struct sockaddr_in *) &recvSockAddr, sizeof(recvSockAddr));

  /* パケット受信 */
  while(1) {
    numrcv = recvfrom(recvSocket, buffer, BUFFER_SIZE, 0, NULL, NULL);
    if(numrcv == -1) { status = closesocket(recvSocket); break; }
    printf("received: %s\n", buffer);
  }

  /* Windows 独自の設定 */
  WSACleanup();
}

UDP クライアント側

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

int
main() {
  /* IPアドレス、ポート番号、ソケット */
  char destination[80];
  unsgined short port = 9876;
  int destSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in destSockAddr;

  /* 各種パラメータ */
  int i;
  char *toSendText = "This is a test";

  /************************************************************/
  /* Windows 独自の設定 */
  WSADATA data;
  WSAStartup(MAKEWORD(2,0), &data));

  /* 相手先アドレスの入力 */
  printf("Connect to ? : (name or IP address) ");
  scanf("%s", destination);

  /* sockaddr_in 構造体のセット */
  memset(&destSockAddr, 0, sizeof(destSockAddr));
  destSockAddr.sin_addr.s_addr = inet_addr(destination);
  destSockAddr.sin_port = htons(port);
  destSockAddr.sin_family = AF_INET;

  /* ソケット生成 */
  destSocket = socket(AF_INET, SOCK_DGRAM, 0);
 
  /* パケット送出 */
  for(i=0; i<10; i++) {
    printf("sending...\n");
    sendto(destSocket, toSendText, strlen(toSendText)+1, 0, &destSockAddr, sizeof(destSockAddr));
    Sleep(1000);
  }

  /* Windows 独自の設定 */
  closesocket(destSocket);
  WSACleanup();
}

 

Linux版: コンパイルは "gcc ソース名 -lsocket" とする。

TCP サーバ側

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define BUFFER_SIZE 256

int
main() {
  /* ポート番号、ソケット */
  unsigned short port = 9876;
  int srcSocket;  // 自分
  int dstSocket;  // 相手

  /* sockaddr_in 構造体 */
  struct sockaddr_in srcAddr;
  struct sockaddr_in dstAddr;
  int dstAddrSize = sizeof(dstAddr);

  /* 各種パラメータ */
  int numrcv;
  char buffer[BUFFER_SIZE];

  /************************************************************/
  /* sockaddr_in 構造体のセット */
  memset(&srcAddr, 0, sizeof(srcAddr));
  srcAddr.sin_port = htons(port);
  srcAddr.sin_family = AF_INET;
  srcAddr.sin_addr.s_addr = htonl(INADDR_ANY);

  /* ソケットの生成 */
  srcSocket = socket(AF_INET, SOCK_STREAM, 0);

  /* ソケットのバインド */
  bind(srcSocket, (struct sockaddr *) &srcAddr, sizeof(srcAddr));

  /* 接続の許可 */
  listen(srcSocket, 1);

  /* 接続の受付け */
  printf("Waiting for connection ...\n");
  dstSocket = accept(srcSocket, (struct sockaddr *) &dstAddr, &dstAddrSize);
  printf("Connected from %s\n", inet_ntoa(dstAddr.sin_addr));

  /* パケット受信 */
  while(1) {
    numrcv = recv(dstSocket, buffer, BUFFER_SIZE, 0); 
    if(numrcv == 0 || numrcv == -1) {
      status = close(dstSocket); break;
    }
    printf("received: %s\n", buffer);
  }
}
 

TCP クライアント側

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

int
main() {
  /* IP アドレス、ポート番号、ソケット */
  char destination[80];
  unsigned short port = 9876;
  int dstSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in dstAddr;

  /* 各種パラメータ */
  int status;
  int numsnt;
  char *toSendText = "This is a test";

  /************************************************************/
  /* 相手先アドレスの入力 */
  printf("Connect to ? : (name or IP address) ");
  scanf("%s", destination);

  /* sockaddr_in 構造体のセット */
  memset(&dstAddr, 0, sizeof(dstAddr));
  dstAddr.sin_port = htons(port);
  dstAddr.sin_family = AF_INET;
  stAddr.sin_addr.s_addr = inet_addr(destination);
 
  /* ソケット生成 */
  dstSocket = socket(AF_INET, SOCK_STREAM, 0);

  /* 接続 */
  printf("Trying to connect to %s: \n", destination);
  connect(dstSocket, (struct sockaddr *) &dstAddr, sizeof(dstAddr));

  /* パケット送出 */
  for(i=0; i<10; i++) {
    printf("sending...\n");
    send(dstSocket, toSendText, strlen(toSendText)+1, 0);
    sleep(1);
  }

  /* ソケット終了 */
  close(dstSocket);
}

UDP サーバ側

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define BUFFER_SIZE 256

int
main() {
  /* ポート番号、ソケット */
  unsigned short port = 9876;
  int recvSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in recvSockAddr;

  /* 各種パラメータ */
  int status;
  int numrcv;
  char buffer[BUFFER_SIZE];
  unsigned long on = 1;

  /************************************************************/
  /* sockaddr_in 構造体のセット */
  memset(&recvSockAddr, 0, sizeof(recvSockAddr));
  recvSockAddr.sin_port = htons(port);
  recvSockAddr.sin_family = AF_INET;
  recvSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

  /* ソケット生成 */
  recvSocket = socket(AF_INET, SOCK_DGRAM, 0);
 
  /* バインド */
  status = bind(recvSocket, (struct sockaddr_in *) &recvSockAddr, sizeof(recvSockAddr));

  /* パケット受信 */
  while(1) {
    numrcv = recvfrom(recvSocket, buffer, BUFFER_SIZE, 0, NULL, NULL);
    if(numrcv == -1) { status = close(recvSocket); break; }
    printf("received: %s\n", buffer);
  }
}

UDP クライアント側

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

int
main() {
  /* IPアドレス、ポート番号、ソケット */
  char destination[80];
  unsgined short port = 9876;
  int destSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in destSockAddr;

  /* 各種パラメータ */
  int i;
  char *toSendText = "This is a test";

  /************************************************************/
  /* 相手先アドレスの入力 */
  printf("Connect to ? : (name or IP address) ");
  scanf("%s", destination);

  /* sockaddr_in 構造体のセット */
  memset(&destSockAddr, 0, sizeof(destSockAddr));
  destSockAddr.sin_addr.s_addr = inet_addr(destination);
  destSockAddr.sin_port = htons(port);
  destSockAddr.sin_family = AF_INET;

  /* ソケット生成 */
  destSocket = socket(AF_INET, SOCK_DGRAM, 0);
 
  /* パケット送出 */
  for(i=0; i<10; i++) {
    printf("sending...\n");
    sendto(destSocket, toSendText, strlen(toSendText)+1, 0, &destSockAddr, sizeof(destSockAddr));
    sleep(1);
  }

  /* ソケットの終了 */
  close(destSocket);
}