Windows 系统 Linux 系统
文件 I/O 函数:fopen 和 fwrite 文件 I/O 函数:open、read 和 write
套接字函数:send
和 recv
套接字函数:(write 和 read) 或 (send 和 recv)或 (writevreadv

之前学习过 Linux 的 send 和 recv 函数,主要学习的部分就是利用其可选项 MSG_OOB 来接收 out-of-band 数据。但是,在 Windows 系统中却没有针对这种事情的处理方法。

好在我们有其他方法可以代替,通过 select 函数来解决这个问题。之前我们通过 select 监视文件描述符,监视对象的类型有

  • 是否存在接受数据的套接字
  • 无需阻塞传输数据的套接字
  • 发生异常的套接字

而这里的 out-of-band 数据就属于 “异常“ 套接字,所以利用 select 函数在 Windows 平台接收数据

send 发送 out-of-band 数据

在发送数据方面,Linux 和 Windows 差别很小

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <WinSock2.h>
  4. #pragma comment(lib, "ws2_32.lib") // 加载 ws2_32.dll
  5. #define BUF_SIZE 30
  6. void ErrorHandling(char *message);
  7. int main(int argc, char *argv[]) {
  8. // 1.检查输入
  9. if (argc != 3) {
  10. printf("Usage: %s <IP><port> \n", argv[0]);
  11. exit(1);
  12. }
  13. // 2.设置版本为 2.2
  14. WSADATA wsaData;
  15. if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  16. ErrorHandling("WSAStartup() error!");
  17. // 3.初始化套接字描述符
  18. SOCKET hSocket;
  19. struct sockaddr_in sendAdr;
  20. hSocket = socket(PF_INET, SOCK_STREAM, 0);
  21. memset(&sendAdr, 0, sizeof(sendAdr));
  22. sendAdr.sin_family = AF_INET;
  23. sendAdr.sin_addr.s_addr = inet_addr(argv[1]);
  24. sendAdr.sin_port = htons(atoi(argv[2]));
  25. // 4.向服务器发送连接请求
  26. if (connect(hSocket, (SOCKADDR*)&sendAdr, sizeof(sendAdr)) == SOCKET_ERROR)
  27. ErrorHandling("connect() error!");
  28. // 5.发送消息
  29. send(hSocket, "123", 3, 0);
  30. send(hSocket, "4", 1, MSG_OOB); // MSG_OOB 紧急发送
  31. send(hSocket, "567", 3, 0);
  32. send(hSocket, "890", 3, MSG_OOB);
  33. closesocket(hSocket);
  34. WSACleanup();
  35. return 0;
  36. }
  37. void ErrorHandling(char *message) {
  38. fputs(message, stderr);
  39. fputc('\n', stderr);
  40. exit(1);
  41. }

接收 out-of-band 数据

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <winsock2.h>
  4. #define BUF_SIZE 30
  5. void ErrorHandling(char *message);
  6. int main(int argc, char *argv[]) {
  7. // 1.检查输入
  8. if (argc != 2) {
  9. printf("Usage: %s <port> \n", argv[0]);
  10. exit(1);
  11. }
  12. // 2.winsock 版本号 2.2
  13. WSADATA wsaData;
  14. if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  15. ErrorHandling("WSAStartup() error!");
  16. // 3.初始化套接字
  17. SOCKET hAcptSock;
  18. struct sockaddr_in recvAdr;
  19. hAcptSock = socket(PF_INET, SOCK_STREAM, 0);
  20. memset(&recvAdr, 0, sizeof(recvAdr));
  21. recvAdr.sin_family = AF_INET;
  22. recvAdr.sin_addr.s_addr = htonl(INADDR_ANY);
  23. recvAdr.sin_port = htons(atoi(argv[1]));
  24. // 4.bind 绑定套接字和端口
  25. if (bind(hAcptSock, (SOCKADDR*)&recvAdr, sizeof(recvAdr)) == SOCKET_ERROR)
  26. ErrorHandling("bind() error");
  27. // 5.listen 监听
  28. if (listen(hAcptSock, 5) == SOCKET_ERROR)
  29. ErrorHandling("listen() error");
  30. // 6.初始化 fd_set,监视客户端对应的套接字
  31. SOCKET hRecvSock;
  32. struct sockaddr_in sendAdr;
  33. int sendAdrSize;
  34. fd_set read, except, readCopy, exceptCopy;
  35. sendAdrSize = sizeof(sendAdr);
  36. hRecvSock = accept(hAcptSock, (SOCKADDR*)&sendAdr, &sendAdrSize); // accept 分配套接字
  37. FD_ZERO(&read);
  38. FD_ZERO(&except);
  39. FD_SET(hRecvSock, &read); // 监听该套接字是否接收数据
  40. FD_SET(hRecvSock, &except); // 监听该套接字是否异常
  41. int strLen;
  42. struct timeval timeout;
  43. int result;
  44. char buf[BUF_SIZE];
  45. while(1) {
  46. readCopy = read;
  47. exceptCopy = except;
  48. timeout.tv_sec = 5;
  49. timeout.tv_usec = 0;
  50. // 7.监听套接字
  51. result = select(0, &readCopy, 0, &exceptCopy, &timeout);
  52. if (result > 0) {
  53. if (FD_ISSET(hRecvSock, &exceptCopy)) { // 7.1.套接字异常,即有 out-of-band 数据
  54. strLen = recv(hRecvSock, buf, BUF_SIZE-1, MSG_OOB);
  55. buf[strLen] = 0;
  56. printf("Urgent message: %s \n", buf);
  57. }
  58. if (FD_ISSET(hRecvSock, &readCopy)) { // 7.2.套接字有数据发送
  59. strLen = recv(hRecvSock, buf, BUF_SIZE-1, 0);
  60. if (strLen == 0) { // 7.2.1. EOF断开连接
  61. break;
  62. closesocket(hRecvSock);
  63. }
  64. else { // 7.2.2. 正常数据
  65. buf[strLen] = 0;
  66. puts(buf);
  67. }
  68. }
  69. }
  70. }
  71. closesocket(hAcptSock);
  72. WSACleanup();
  73. return 0;
  74. }
  75. void ErrorHandling(char *message) {
  76. fputs(message, stderr);
  77. fputc('\n', stderr);
  78. exit(1);
  79. }

编译程序:

  1. gcc send.c -o send.exe -lwsock32
  2. gcc recv.c -o recv.exe -lwsock32

运行程序,服务器端:

§ Windows 中的 send & recv - 图1

客户端:

§ Windows 中的 send & recv - 图2