C言語-ソケットプログラミング_双方向通信_TCPクライアント
TCPによる双方向通信(クライアント側)
双方向通信とは
双方向通信とは、応答を待ち受ける通信のことです。
送信側(クライアント)からデータを受信側(サーバ)に送り、その後サーバからの応答を求める仕様となります。
プログラム説明
エコーサーバと呼ばれるプログラムです。
- クライアントはメッセージをサーバに送信します。
- サーバから同一メッセージが返ってくるのを待ち受けます。
- サーバから送られたメッセージを標準出力します。
受信時のrecv()関数について
下記プログラムでは、簡易的にrecv()は一回のみ実行しています。
本来は、受信データの取り扱い(アプリケーションプロトコル)を決めておく必要があります。
recv()は届いているデータをバッファから取り出す処理を行います。
そこに「データが届いた」か知る機能はありません。
例えば、recv()で10KBのバッファを用意して10KBのデータを受信しようとしても、2KB、4KB、4KBと受信が行われる場合もありえます。
この場合には、計3回recv()を呼び出さないといけないことになります。
どれだけのデータを通信するのかは、あらかじめメッセージ長を決めておくか、データがなくなる(コネクションを切断する)まで受信するという風に決めておく必要があります。
例えば、HTTPでは、ヘッダ中にデータの長さが書いてあります。
プログラム
/*
* TCP client
*/
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
/*
* クライアントの接続先サーバ情報
*/
struct client_info {
unsigned short sv_port; /* サーバのポート番号 */
char *sv_ipaddr; /* サーバのIPアドレス */
char *msg; /* 送信メッセージ */
int sd; /* ソケットディスクリプタ */
struct sockaddr_in sv_addr; /* サーバのアドレス構造体 */
};
typedef struct client_info cl_info_t;
/*!
* @brief 応答メッセージを受信する
* @param[in] info クライアント接続情報
* @param[out] errmsg エラーメッセージ格納先
* @return 成功ならば0、失敗ならば-1を返す。
*/
static int
tcp_receive_msg(cl_info_t *info, char *errmsg)
{
int recv_msglen = 0;
char buff[BUFSIZ];
/* 応答メッセージを受信する */
recv_msglen = recv(info->sd, buff, BUFSIZ, 0);
if(recv_msglen < 0){
sprintf(errmsg, "(line:%d) %s", __LINE__, strerror(errno));
return(-1);
}
/* 応答メッセージを出力する */
buff[recv_msglen] = '\0'; /* null-terminate */
fprintf(stdout, "Received: %s\n", buff);
return(0);
}
/*!
* @brief TCP接続してメッセージを送る
* @param[in] info クライアント接続情報
* @param[out] errmsg エラーメッセージ格納先
* @return 成功ならば0、失敗ならば-1を返す。
*/
static int
tcp_send_msg(cl_info_t *info, char *errmsg)
{
int rc = 0;
int msg_len = strlen(info->msg) + 1;
/* メッセージの送信 */
rc = send(info->sd, info->msg, msg_len, 0);
if(rc != msg_len){
sprintf(errmsg, "(line:%d) %s", __LINE__, strerror(errno));
return(-1);
}
return(0);
}
/*!
* @brief エコークライアントを実行する
* @param[in] info クライアント接続情報
* @param[out] errmsg エラーメッセージ格納先
* @return 成功ならば0、失敗ならば-1を返す。
*/
static int
tcp_echo_client(cl_info_t *info, char *errmsg)
{
int rc = 0;
rc = connect(info->sd, (struct sockaddr *)&(info->sv_addr),
sizeof(info->sv_addr));
if(rc != 0){
sprintf(errmsg, "(line:%d) %s", __LINE__, strerror(errno));
return(-1);
}
/* メッセージを送信する */
rc = tcp_send_msg(info, errmsg);
if(rc != 0) return(-1);
/* 応答メッセージを受信する */
rc = tcp_receive_msg(info, errmsg);
if(rc != 0) return(-1);
return(0);
}
/*!
* @brief ソケットの初期化
* @param[in] info クライアント接続情報
* @param[out] errmsg エラーメッセージ格納先
* @return 成功ならば0、失敗ならば-1を返す。
*/
static int
socket_initialize(cl_info_t *info, char *errmsg)
{
/* ソケットの生成 : TCPを指定する */
info->sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(info->sd < 0){
sprintf(errmsg, "(line:%d) %s", __LINE__, strerror(errno));
return(-1);
}
/* サーバのアドレス構造体を作成する */
info->sv_addr.sin_family = AF_INET;
info->sv_addr.sin_addr.s_addr = inet_addr(info->sv_ipaddr);
info->sv_addr.sin_port = htons(info->sv_port);
return(0);
}
/*!
* @brief ソケットの終期化
* @param[in] info クライアント接続情報
* @return 成功ならば0、失敗ならば-1を返す。
*/
static void
socket_finalize(cl_info_t *info)
{
/* ソケット破棄 */
if(info->sd != 0) close(info->sd);
return;
}
/*!
* @brief TCPクライアント実行
* @param[in] info クライアント接続情報
* @param[out] errmsg エラーメッセージ格納先
* @return 成功ならば0、失敗ならば-1を返す。
*/
static int
tcp_client(cl_info_t *info, char *errmsg)
{
int rc = 0;
/* ソケットの初期化 */
rc = socket_initialize(info, errmsg);
if(rc != 0) return(-1);
/* メッセージの送受信を行う */
rc = tcp_echo_client(info, errmsg);
/* ソケットの終期化 */
socket_finalize(info);
return(rc);
}
/*!
* @brief 初期化処理。IPアドレスとポート番号を設定する。
* @param[in] argc コマンドライン引数の数
* @param[in] argv コマンドライン引数
* @param[out] info クライアント接続情報
* @param[out] errmsg エラーメッセージ格納先
* @return 成功ならば0、失敗ならば-1を返す。
*/
static int
initialize(int argc, char *argv[], cl_info_t *info, char *errmsg)
{
if(argc != 4){
sprintf(errmsg, "Usage: %s <ip-addr> <port> <msg>", argv[0]);
return(-1);
}
memset(info, 0, sizeof(cl_info_t));
info->sv_ipaddr = argv[1];
info->sv_port = atoi(argv[2]);
info->msg = argv[3];
return(0);
}
/*!
* @brief main routine
* @return 成功ならば0、失敗ならば-1を返す。
*/
int
main(int argc, char *argv[])
{
int rc = 0;
cl_info_t info = {0};
char errmsg[BUFSIZ];
rc = initialize(argc, argv, &info, errmsg);
if(rc != 0){
fprintf(stderr, "Error: %s\n", errmsg);
return(-1);
}
rc = tcp_client(&info, errmsg);
if(rc != 0){
fprintf(stderr, "Error: %s\n", errmsg);
return(-1);
}
return(0);
}
関連ページ
UDP通信
- C言語-ソケットプログラミング_片方向通信_UDPクライアント
- C言語-ソケットプログラミング_片方向通信_UDPサーバ
- C言語-ソケットプログラミング_双方向通信_UDPクライアント
- C言語-ソケットプログラミング_双方向通信_UDPサーバ
TCP通信
- C言語-ソケットプログラミング_片方向通信_TCPクライアント
- C言語-ソケットプログラミング_片方向通信_TCPサーバ
- C言語-ソケットプログラミング_双方向通信_TCPクライアント
- C言語-ソケットプログラミング_双方向通信_TCPサーバ