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 30
void 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.2
WSADATA 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 30
void 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.2
WSADATA 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 -lwsock32
gcc recv.c -o recv.exe -lwsock32
运行程序,服务器端:
客户端: