【C/C++】TCPサーバ(簡易版)(IPv6対応済)

概要

TCPサーバポート「12345」にて接続を受け付け、クライアントから受信したデータをクライアントへ送信する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")

#define DEFAULT_BUFLEN 512
#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_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;

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

	// ソケット作成
	SOCKET listenSocket = INVALID_SOCKET;
	listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (listenSocket == INVALID_SOCKET)
	{
		printf("socket failed with error: %ld\n", WSAGetLastError());
		freeaddrinfo(result);
		WSACleanup();
		return 1;
	}

	// ソケット割当
	ret = bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);
	if (ret == SOCKET_ERROR)
	{
		printf("bind failed with error: %d\n", WSAGetLastError());
		freeaddrinfo(result);
		closesocket(listenSocket);
		WSACleanup();
		return 1;
	}

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

	// 接続受付開始
	ret = listen(listenSocket, SOMAXCONN);
	if (ret == SOCKET_ERROR)
	{
		printf("listen failed with error: %d\n", WSAGetLastError());
		closesocket(listenSocket);
		WSACleanup();
		return 1;
	}

	// クライアント情報
	struct sockaddr_in clientAddr;
	int clientAddrLen = sizeof(clientAddr);

	// 接続受付
	SOCKET clientSocket = INVALID_SOCKET;
	clientSocket = accept(listenSocket, (struct sockaddr *)&clientAddr, &clientAddrLen);
	if (clientSocket == INVALID_SOCKET)
	{
		printf("accept failed with error: %d\n", WSAGetLastError());
		closesocket(listenSocket);
		WSACleanup();
		return 1;
	}

	// クライアント情報
	char str_buffer[max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
	inet_ntop(clientAddr.sin_family, &clientAddr.sin_addr, str_buffer, sizeof(str_buffer));
	printf("accepted connection from %s, port=%d\n",
		str_buffer,
		ntohs(clientAddr.sin_port)
	);

	// 接続受付ソケット解放
	closesocket(listenSocket);

	// 受信
	for (;;)
	{
		// 受信
		char recvbuf[DEFAULT_BUFLEN];
		ret = recv(clientSocket, recvbuf, sizeof(recvbuf), 0);
		if (ret > 0)
		{
			printf("Bytes received: %d\n", ret);

			// 送信
			ret = send(clientSocket, recvbuf, ret, 0);
			if (ret == SOCKET_ERROR)
			{
				printf("send failed with error: %d\n", WSAGetLastError());
				closesocket(clientSocket);
				WSACleanup();
				return 1;
			}
			printf("Bytes sent: %d\n", ret);
		}
		else if (ret == 0)
		{
			printf("Connection closing...\n");
			break;
		}
		else
		{
			printf("recv failed with error: %d\n", WSAGetLastError());
			closesocket(clientSocket);
			WSACleanup();
			return 1;
		}
	}

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

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

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

	return 0;
}

各関数の解説

WSAStartup 関数

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

getaddrinfo 関数

自分自身の指定したポート番号のアドレス情報を取得する。

freeaddrinfo 関数

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

socket 関数

ソケットを作成する。

bind 関数

ソケットとネットワークインターフェース(LANアダプタ)を紐づける。

listen 関数

クライアントから接続の受け付けを開始する。

accept 関数

クライアントから受け付けた接続済ソケットを取得する。

send 関数

クライアントへ送信する。

recv 関数

クライアントから受信する。

shutdown 関数

クライアントへ切断を通知する。

closesocket 関数

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

WSACleanup 関数

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

おすすめの記事