库说明
Win32 进行网络编程需要使用到 ws2_32.lib
库,它是 Windows Sockets 2 (Winsock2) 的库文件,其主要头文件为winsock2.h
。如果使用 Windows.h
头文件则默认包含 winsock.h
,他会和 winsock2.h
冲突。可以通过在包含 Windows.h 之前定义宏 WIN32_LEAN_AND_MEAN
来排除一些不常用的 Windows 头文件,这里面就会排除掉 winsock.h
。
// 链接 Winsock 库
#pragma comment(lib, "WS2_32.lib") //排除掉 Windows.h 中包含的一些不常用头文件
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
常用API
API | 说明 | 参数 |
---|---|---|
WSAStartup | 初始化WinSock库 | wVersionRequired:版本号,MAKEWORD(2, 2)表示2.2版本 lpWSAData:存储关于 Winsock 的信息 |
WSACleanup | 清理WinSock库 | |
socket | 创建一个socket | domain:指定socket使用的地址族(AF_INET、AF_INET6等) type:socket类型SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)等 protocol:使用的协议IPPROTO_TCP、IPPROTO_UDP,0表示自动选择 返回值:socket的文件描述符号 |
bind | 将socket与一个特定的地址和端口绑定 | sockfd:socket文件描述符 addr:包含地址信息的结构体的指针 addrlen:addr结构体长度 返回值:成功时返回 0,失败时返回 SOCKET_ERROR |
listen | 将socket转换为监听状态 | sockfd:socket文件描述符 backlog:等待接受连接的客户端的最大数量,超过将被拒绝 |
accept | 接受客户端的连接请求,并创建一个新的套接字用于与客户端进行通信 | sockfd:socket文件描述符 addr:输出参数,用于纪录客户端的地址 addrlen:输出参数,表示addr的实际长度 返回值:返回一个新的套接字文件描述符,如果出现错误返回 SOCKET_ERROR |
connect | 请求连接服务器 | s:标识要连接的 socket name:服务器的地址信息 namelen:服务器地址信息结构体大小 |
send | 发送内容到socket | s:socket文件描述符 buf:发送数据的缓冲区 len:需要发送字节数 flags:控制接收操作的行为,通常为 0 返回值:返回发送的字节数。如果出现错误,返回 SOCKET_ERROR |
recv | 从已连接的socket接收数据 | sockfd:socket文件描述符 buf:接收数据的缓冲区 len:缓冲区大小,可以接收最大字节数 flags:控制接收操作的行为,通常为 0 返回值:返回接收的字节数。如果连接已关闭,返回 0。如果出现错误,返回 SOCKET_ERROR |
closesocket | 关闭socket | |
inet_pton | 将点分十进制的IP转换为Long 它不是标准 Winsock 函数 它是在高版本中定义的 需要引用 Ws2tcpip.h 头文件 | af:地址族(AF_INET、AF_INET6) src:点分十进制字符串指针 转换后的二进制地址 |
inet_ntop | 将Long类型的IP转换为点分十进制 | |
htons | 将16位主机字节序转换为网络字节序 网络字节序都采用大端字节序 用于端口号转换 | |
htonl | 将32位主机字节序转换为网络字节序 网络字节序都采用大端字节序 用于IP转换 |
服务端
static DWORD WINAPI serverRecv(LPVOID lpParameter) {int* clientSocket = (int*)lpParameter;char readBuffer[512];while (true) {memset(readBuffer, 0, 512);int readSize = recv(*clientSocket, readBuffer, 512, 0);if (readSize <= 0) {perror("recv");closesocket(*clientSocket);return 0;}printf("read buffer = %s", readBuffer);}return 0;
}static DWORD WINAPI server(LPVOID lpParameter) {// 初始化 Winsock 2.2 版本WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {printf("Failed to initialize Winsock\n");}int serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (serverSocket == INVALID_SOCKET) {perror("socket");}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;serverAddr.sin_port = htons(12345);if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {perror("bind");}if (listen(serverSocket, SOMAXCONN) == -1) {perror("listen");}while (TRUE) {sockaddr_in clientAddr;int* addrlen = NULL;int clientSocket = accept(serverSocket, (struct sockaddr*)&serverAddr, addrlen);if (clientSocket == -1) {perror("accept");continue;}CreateThread(NULL, 0, serverRecv, &clientSocket, 0, NULL);}return 0;
}
客户端
static DWORD WINAPI client(LPVOID lpParameter) {WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {printf("Failed to initialize Winsock\n");}printf("create client socket \n");int clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (clientSocket == INVALID_SOCKET) {perror("socket");}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(12345);inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);if (connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {fprintf(stderr, "Failed to connect to server\n");}const char* message = "Hello, Server!";if (send(clientSocket, message, strlen(message), 0) == SOCKET_ERROR) {fprintf(stderr, "Failed to send data to server\n");}else {printf("Data sent to server: %s\n", message);}return 0;
}