windows 平台 TCP 网络通信-服务端流程

  1. 创建 Socket 套接字.
  2. Socket 套接字绑定一个端口号.
  3. Socket 套接字开启监听属性,只能接收连接.
  4. 等待客户端连接.
  5. 开始通讯.
  6. 关闭连接.
+ 代码示例
#include <stdio.h>
#include <string.h>
#include <WinSock2.h>
#include <ws2tcpip.h>

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

DWORD WINAPI thread_func(LPVOID lpThreadParameter) {
	SOCKET client_socket = *(SOCKET*)lpThreadParameter;
	free(lpThreadParameter);
	// 解决只能发送一条消息的问题.
	while (1) {
		// 5. 开始通讯
		/**
		* int recv (
			SOCKET s,		// 客户端socket
			char * buf,		// 接收的数据存储
			int len,		// 接收的长度
			int flags		// 0
		  );
		*/
		char buffer[1024] = { 0 };
		int ret = recv(client_socket, buffer, 1024, 0);
		if (ret <= 0) break;
		printf("%llu: %s\n", client_socket, buffer);
		send(client_socket, buffer, (int)strlen(buffer), 0);
	}
	// 6. 关闭连接
	/**
	* int closesocket(
		SOCKET s
	  );
	*/
	printf("socket: %llu, disconnect.\n", client_socket);
	closesocket(client_socket);
	return 0;
}

int main() {

	// windows 端使用网络功能需要开启网络权限
	WSADATA wsaData;
	// #define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
	// 把两个 BYTE(8 位) 组合成一个 WORD(16 位)
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	// 1. 创建 socket 套接字
	/**
	* SOCKET socket(
		int af,			// 协议地址簇
		int type,		// 类型 流式协议/帧式协议
		int protocol	// 保护协议 0 
	  );
	*/
	SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == listen_socket) {
		printf("create listen socket failed !!! errcode: %d\n", GetLastError());
		return -1;
	}
	// 2. 给这个 socket 套接字绑定一个端口号
	/**
	* typedef struct sockaddr_in {
		short   sin_family;	// 协议地址簇 IPV4/IPV6 AF_INET/AF_INET6
		USHORT sin_port;	// 端口号
		IN_ADDR sin_addr;	// IP地址
		CHAR sin_zero[8];	// 保留字节
	  };
	*/

	struct sockaddr_in local = { 0 };
	local.sin_family = AF_INET;
	// 8080对应16进制:1F90.
	// 大端序模式:高位在前,低位在后. (千百十个,正常的阅读顺序).
	// 小端模式: 低位在前,高位在后. (本地PC电脑以小端序存储数据).
	// htons: short类型
	local.sin_port = htons(8080);		// 大小端问题
	// htol: long类型.
	// INADDR_ANY: 监听所有网卡的该端口.
	//local.sin_addr.s_addr = htonl(INADDR_ANY);	// 服务端, 只接受哪个网卡的数据.
	// inet_addr(废弃的API): 字符串IP地址转化为整数IP地址.
	// 如果使用 inet_addr,需要加 #define _WINSOCK_DEPRECATED_NO_WARNINGS .
	//local.sin_addr.s_addr = inet_addr("0.0.0.0");
	InetPton(AF_INET, TEXT("0.0.0.0"), &local.sin_addr);

	/**
	* int bind(
		SOCKET s,
		const struct sockaddr  * name,
		int namelen
	  )
	*/
	if (-1 == bind(listen_socket, (struct sockaddr*)&local, sizeof(local))) {
		printf("bind socket failed !!! errcode: %d\n", GetLastError());
		return -1;
	}

	// 3. 给这个socket 开启监听属性
	/**
	* int listen(
		SOCKET s,
		int backlog
	  );
	*/
	if (-1 == listen(listen_socket, 10)) {
		printf("start listen socket failed !!! errcode: %d\n", GetLastError());
		return -1;
	}

	// 4. 等待客户端连接
	/**
	* SOCKET accept(
		SOCKET s,					// 监听socket
		struct sockaddr * addr,		// 客户端的IP地址和端口号
		int * addrlen				// 结构大小
	  );
	*/
	// client_socket: 通讯 Socket,服务端与客户端通过该 socket 通信.

	while (1) {
		SOCKET client_socket = accept(listen_socket, NULL, NULL);
		if (INVALID_SOCKET == client_socket) {
			continue;
		}
		printf("new connect.socket:%llu\n", client_socket);
		SOCKET* sockfd = (SOCKET*)malloc(sizeof(SOCKET));
		*sockfd = client_socket;
		// 创建线程, 解决只能和一个客户端通讯的问题.
		CreateThread(NULL, 0, thread_func, sockfd, 0, NULL);
	}
}

windows 平台 TCP 网络通信-客户端流程

  1. 创建 socket 套接字.
  2. 连接服务器.
  3. 开始通讯.
  4. 关闭连接.
+ 代码示例
#include <stdio.h>
#include <WinSock2.h>
#include <ws2tcpip.h>

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

int main() {
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	
	// 1. 创建 socket 套接字
	SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == client_socket) {
		printf("create socket failed !!!\n");
		return -1;
	}

	// 2. 连接服务器

	struct  sockaddr_in target;
	target.sin_family = AF_INET;
	target.sin_port = htons(8080);
	//target.sin_addr.s_addr = inet_addr("127.0.0.1");
	InetPton(AF_INET, TEXT("127.0.0.1"), &target.sin_addr);
	if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target))) {
		printf("connect server failed !!!\n");
		closesocket(client_socket);
		return -1;
	}

	// 3. 开始通讯
	while (1) {
		char sbuffer[1024] = { 0 };
		printf("Please enter: ");
		// 使用 scanf 不安全,需要加 #define _CRT_SECURE_NO_WARNINGS 关闭警告.
		//scanf("%s", sbuffer);
		scanf_s("%99s", sbuffer, (unsigned)_countof(sbuffer));
		send(client_socket, sbuffer, strlen(sbuffer), 0);
		char rbuffer[1024] = { 0 };
		int ret = recv(client_socket, rbuffer, 1024, 0);
		if (ret <= 0) {
			break;
		}
		printf("%s\n", rbuffer);
	}

	// 4. 关闭连接
	closesocket(client_socket);
	
	return 0;
}