【C/C++】TCPクライアント(簡易版)(IPv6対応済)

概要

自コンピュータのTCPサーバポート「12345」に接続し、送信と受信を各1回ずつ行うTCPクライアントプログラムのサンプルコードです。

サンプルコード

#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>

#pragma comment (lib, "ws2_32.lib")
#pragma comment (lib, "mswsock.lib")
#pragma comment (lib, "advapi32.lib")

#define DEFAULT_BUFLEN (4 * 1024)
#define DEFAULT_TARGET "localhost"
#define DEFAULT_PORT "12345"

int main()
{
	int ret = 0;

	// WINSOCKライブラリ初期化
	WSADATA wsadata;
	ret = WSAStartup(WINSOCK_VERSION, &wsadata);
	if (ret != 0)
	{
		printf("WSAStartup failed with error: %d\n", ret);
		return 1;
	}

	// ソケット情報
	struct addrinfo	hints;
	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	// アドレス情報取得
	struct addrinfo *result = NULL;
	ret = getaddrinfo(DEFAULT_TARGET, DEFAULT_PORT, &hints, &result);
	if (ret != 0)
	{
		printf("getaddrinfo failed with error: %d\n", ret);
		WSACleanup();
		return 1;
	}

	// 接続
	SOCKET connectSocket = INVALID_SOCKET;
	for (struct addrinfo *ptr = result; ptr != NULL; ptr = ptr->ai_next)
	{
		// ソケット作成
		connectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
		if (connectSocket == INVALID_SOCKET)
		{
			printf("socket failed with error: %ld\n", WSAGetLastError());
			WSACleanup();
			return 1;
		}

		// 接続
		ret = connect(connectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
		if (ret == SOCKET_ERROR)
		{
			closesocket(connectSocket);
			connectSocket = INVALID_SOCKET;
			continue;
		}
		break;
	}

	// アドレス情報解放
	freeaddrinfo(result);
	result = NULL;

	// 接続確認
	if (connectSocket == INVALID_SOCKET)
	{
		printf("Unable to connect to server!\n");
		WSACleanup();
		return 1;
	}

	// 送信
	const char *sendbuf = "this is a test";
	ret = send(connectSocket, sendbuf, (int)strlen(sendbuf), 0);
	if (ret == SOCKET_ERROR)
	{
		printf("send failed with error: %d\n", WSAGetLastError());
		closesocket(connectSocket);
		WSACleanup();
		return 1;
	}
	printf("Bytes Sent: %ld\n", ret);

	// 受信
	char recvbuf[DEFAULT_BUFLEN];
	ret = recv(connectSocket, recvbuf, sizeof(recvbuf), 0);
	if (ret > 0)
	{
		printf("Bytes received: %d\n", ret);
	}
	else if (ret == 0)
	{
		printf("Connection closed\n");
		closesocket(connectSocket);
		WSACleanup();
		return 1;
	}
	else
	{
		printf("recv failed with error: %d\n", WSAGetLastError());
		closesocket(connectSocket);
		WSACleanup();
		return 1;
	}

	// 切断要求
	ret = shutdown(connectSocket, SD_SEND);
	if (ret == SOCKET_ERROR)
	{
		printf("shutdown failed with error: %d\n", WSAGetLastError());
		closesocket(connectSocket);
		WSACleanup();
		return 1;
	}

	// 切断待ち
	for (;;)
	{
		// 受信
		ret = recv(connectSocket, recvbuf, sizeof(recvbuf), 0);
		if (ret > 0)
		{
			printf("Bytes received: %d\n", ret);
		}
		else if (ret == 0)
		{
			printf("Connection closed\n");
			break;
		}
		else
		{
			printf("recv failed with error: %d\n", WSAGetLastError());
			break;
		}
	}

	// ソケット解放
	closesocket(connectSocket);

	// WINSOCKライブラリ解放
	WSACleanup();

	return 0;
}

各関数の解説

WSAStartup 関数

WinSockライブラリを初期化する。
プロセス開始時に1度だけ実行すること

getaddrinfo 関数

サーバのアドレス情報を取得する。
第1引数には、接続先のIPアドレス、又はドメイン名を設定する。
第2引数には、接続先のポート番号を設定する。
第3引数には、取得するアドレス情報の基準を設定する。
第4引数には、取得したアドレス情報を格納する領域のポインタを設定する。ここにアドレス情報が格納される。

freeaddrinfo 関数

getaddrinfo 関数にて取得したアドレス情報を開放する。
不要になったアドレス情報は、必ず本関数で解放すること

socket 関数

ソケットを作成する。

connect 関数

サーバへ接続する。

send 関数

サーバへ送信する。

recv 関数 (shutdown 関数実行前)

サーバから受信する。

shutdown 関数

サーバへ切断を通知する。

recv 関数 (shutdown 関数実行後)

切断を待つ。

closesocket 関数

ソケットを開放する。
不要になったソケットは、必ず本関数で解放すること

WSACleanup 関数

WinSockライブラリを開放する。
プロセス終了時に1度だけ実行すること

おすすめの記事