| Windows 系统 | Linux 系统 |
|---|---|
| 文件 I/O 函数:fopen 和 fwrite | 文件 I/O 函数:open、read 和 write |
| 套接字函数:send 和 recv |
套接字函数:(write 和 read) 或 (send 和 recv)或 (writev 和 readv) |
之前学习过 Linux 的 send 和 recv 函数,主要学习的部分就是利用其可选项 MSG_OOB 来接收 out-of-band 数据。但是,在 Windows 系统中却没有针对这种事情的处理方法。
好在我们有其他方法可以代替,通过 select 函数来解决这个问题。之前我们通过 select 监视文件描述符,监视对象的类型有
- 是否存在接受数据的套接字
- 无需阻塞传输数据的套接字
- 发生异常的套接字
而这里的 out-of-band 数据就属于 “异常“ 套接字,所以利用 select 函数在 Windows 平台接收数据
send 发送 out-of-band 数据
在发送数据方面,Linux 和 Windows 差别很小
#include <stdio.h>#include <stdlib.h>#include <WinSock2.h>#pragma comment(lib, "ws2_32.lib") // 加载 ws2_32.dll#define BUF_SIZE 30void ErrorHandling(char *message);int main(int argc, char *argv[]) {// 1.检查输入if (argc != 3) {printf("Usage: %s <IP><port> \n", argv[0]);exit(1);}// 2.设置版本为 2.2WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error!");// 3.初始化套接字描述符SOCKET hSocket;struct sockaddr_in sendAdr;hSocket = socket(PF_INET, SOCK_STREAM, 0);memset(&sendAdr, 0, sizeof(sendAdr));sendAdr.sin_family = AF_INET;sendAdr.sin_addr.s_addr = inet_addr(argv[1]);sendAdr.sin_port = htons(atoi(argv[2]));// 4.向服务器发送连接请求if (connect(hSocket, (SOCKADDR*)&sendAdr, sizeof(sendAdr)) == SOCKET_ERROR)ErrorHandling("connect() error!");// 5.发送消息send(hSocket, "123", 3, 0);send(hSocket, "4", 1, MSG_OOB); // MSG_OOB 紧急发送send(hSocket, "567", 3, 0);send(hSocket, "890", 3, MSG_OOB);closesocket(hSocket);WSACleanup();return 0;}void ErrorHandling(char *message) {fputs(message, stderr);fputc('\n', stderr);exit(1);}
接收 out-of-band 数据
#include <stdio.h>#include <stdlib.h>#include <winsock2.h>#define BUF_SIZE 30void ErrorHandling(char *message);int main(int argc, char *argv[]) {// 1.检查输入if (argc != 2) {printf("Usage: %s <port> \n", argv[0]);exit(1);}// 2.winsock 版本号 2.2WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error!");// 3.初始化套接字SOCKET hAcptSock;struct sockaddr_in recvAdr;hAcptSock = socket(PF_INET, SOCK_STREAM, 0);memset(&recvAdr, 0, sizeof(recvAdr));recvAdr.sin_family = AF_INET;recvAdr.sin_addr.s_addr = htonl(INADDR_ANY);recvAdr.sin_port = htons(atoi(argv[1]));// 4.bind 绑定套接字和端口if (bind(hAcptSock, (SOCKADDR*)&recvAdr, sizeof(recvAdr)) == SOCKET_ERROR)ErrorHandling("bind() error");// 5.listen 监听if (listen(hAcptSock, 5) == SOCKET_ERROR)ErrorHandling("listen() error");// 6.初始化 fd_set,监视客户端对应的套接字SOCKET hRecvSock;struct sockaddr_in sendAdr;int sendAdrSize;fd_set read, except, readCopy, exceptCopy;sendAdrSize = sizeof(sendAdr);hRecvSock = accept(hAcptSock, (SOCKADDR*)&sendAdr, &sendAdrSize); // accept 分配套接字FD_ZERO(&read);FD_ZERO(&except);FD_SET(hRecvSock, &read); // 监听该套接字是否接收数据FD_SET(hRecvSock, &except); // 监听该套接字是否异常int strLen;struct timeval timeout;int result;char buf[BUF_SIZE];while(1) {readCopy = read;exceptCopy = except;timeout.tv_sec = 5;timeout.tv_usec = 0;// 7.监听套接字result = select(0, &readCopy, 0, &exceptCopy, &timeout);if (result > 0) {if (FD_ISSET(hRecvSock, &exceptCopy)) { // 7.1.套接字异常,即有 out-of-band 数据strLen = recv(hRecvSock, buf, BUF_SIZE-1, MSG_OOB);buf[strLen] = 0;printf("Urgent message: %s \n", buf);}if (FD_ISSET(hRecvSock, &readCopy)) { // 7.2.套接字有数据发送strLen = recv(hRecvSock, buf, BUF_SIZE-1, 0);if (strLen == 0) { // 7.2.1. EOF断开连接break;closesocket(hRecvSock);}else { // 7.2.2. 正常数据buf[strLen] = 0;puts(buf);}}}}closesocket(hAcptSock);WSACleanup();return 0;}void ErrorHandling(char *message) {fputs(message, stderr);fputc('\n', stderr);exit(1);}
编译程序:
gcc send.c -o send.exe -lwsock32gcc recv.c -o recv.exe -lwsock32
运行程序,服务器端:

客户端:

