当前位置: 首页 > news >正文

《TCP/IP网络编程》(第十三章)多种I/O函数(2)

使用readvwritev函数可以提高数据通信的效率,它们的功能可以概括为**“对数据进行整合传输及发送”**。
即使用writev函数可以将分散在多个缓冲中的数据一并发送,使用readv函数可以由多个缓冲分别接受,所以适当使用他们可以减少I/O函数的调用次数。

1.readvwritev函数

readv()函数
用于从文件描述符中读取数据,并存储在多个缓冲区中

ssize_t readv(
int fd, //文件描述符。
const struct iovec *iov, //指向 iovec 结构体数组的指针,iovec 结构体定义了一个缓冲区。
int iovcnt//iovec 结构体数组的元素个数。
);//iovec 结构体
struct iovec {void *iov_base; //指向缓冲区的起始地址(基地址)size_t iov_len; //缓冲区的长度,即需要传输的字节数
};

示例代码

#include<stdio.h>
#include<sys/uio.h>
#define BUF_SIZE 100
int main(int argc, char *argv[])
{struct iovec vec[2];char buf1[BUF_SIZE] = {0, };char buf2[BUF_SIZE] = {0, };int str_len;//设置两个缓存区,第一个存储5个字节,剩下的给第二个缓冲区vec[0].iov_base = buf1;vec[0].iov_len = 5;vec[1].iov_base = buf2;vec[1].iov_len = BUF_SIZE;str_len = readv(0, vec, 2);//第一个参数是0,即接受键盘输入printf("Read total bytes: %d \n", str_len);printf("First message: %s \n", buf1);printf("Second message: %s \n", buf2);return 0;
}

在这里插入图片描述

writev()函数
用于将多个缓冲区中的数据写入文件描述符,这种方法称为“聚集写”或“向量写”。

ssize_t writev(
int fd, //文件描述符
const struct iovec *iov, //指向 iovec 结构体数组的指针,iovec 结构体定义了一个缓冲区。
int iovcnt//iovec 结构体数组的元素个数。
);//iovec 结构体
struct iovec {void *iov_base; //指向缓冲区的起始地址(基地址)size_t iov_len; //缓冲区的长度,即需要传输的字节数
};

示例代码

#include<stdio.h>
#include<sys/uio.h>int main(int argc, char *argv[]){struct iovec vec[2];//有两个缓冲char buf1[] = "1234567890";char buf2[] = "ABCDEFGHIJ";int str_len;vec[0].iov_base = buf1;vec[0].iov_len = 10;vec[1].iov_base = buf2;vec[1].iov_len = 10;str_len = writev(1, vec, 2);//第一个参数是1,所以是向控制台输出puts("");printf("writev bytes: %d \n", str_len);
}

在这里插入图片描述

2.在Windows中实现紧急消息机制

在《TCP/IP网络编程》(第十三章)多种I/O函数(1)中已经基于Linux平台实现了MSG_OOB机制,但是是基于Linux的信号处理机制,所以无法直接移植到Windows平台。

若要在Windows平台上实现该机制,则需要通过select()函数,该函数简介参考《TCP/IP网络编程》(第十二章)I/O复用(1)

PS:MSG_OOB在select()监视下会被视为异常数据
发送端代码

#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>#define BUFSIZE 30
void ErrorHandling(char* message);int main(int argc, char* argv[]){WSADATA wsa;SOCKET hSocket;SOCKADDR_IN sendAddr;if(argc!=3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2,2), &wsa) != 0){ErrorHandling("WSAStartup() error!");}hSocket = socket(PF_INET, SOCK_STREAM, 0);memset(&sendAddr, 0, sizeof(sendAddr));sendAddr.sin_family = AF_INET;sendAddr.sin_addr.s_addr = inet_addr(argv[1]);sendAddr.sin_port = htons(atoi(argv[2]));if(connect(hSocket, (SOCKADDR*)&sendAddr, sizeof(sendAddr)) == SOCKET_ERROR)ErrorHandling("connect() error!");send(hSocket, "123", 3, 0);send(hSocket, "4", 1, MSG_OOB);//带外数据在select的监视中,会被视为异常数据send(hSocket, "567", 3, 0);send(hSocket, "890", 3, MSG_OOB);//只把最后一个字节0作为紧急信息发送closesocket(hSocket);WSACleanup();return 0;
}void ErrorHandling(char* message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}

接受端代码

#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>#define BUFSIZE 30
void ErrorHandling(char *);int main(int argc, char *argv[]){WSADATA wsa;SOCKET hAcptSock, hRecvSock;SOCKADDR_IN sendAdr, recvAdr;int sendAdrSz,strLen;char buf[BUFSIZE];int result;fd_set read,except,read_copy,except_copy;struct timeval timeout;if(argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2,2), &wsa) != 0)ErrorHandling("WSAStartup() error!");hAcptSock = socket(PF_INET, SOCK_STREAM, 0);if(hAcptSock == INVALID_SOCKET)ErrorHandling("socket() error");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]));if(bind(hAcptSock, (SOCKADDR*)&recvAdr, sizeof(recvAdr)) == SOCKET_ERROR)ErrorHandling("bind() error");if(listen(hAcptSock, 5) == SOCKET_ERROR)ErrorHandling("listen() error");sendAdrSz = sizeof(sendAdr);hRecvSock = accept(hAcptSock, (SOCKADDR*)&sendAdr, &sendAdrSz);FD_ZERO(&read);    // 清空reads集合FD_ZERO(&except);  // 清空except集合FD_SET(hRecvSock, &read);		// 将套接字添加到reads集合FD_SET(hRecvSock, &except);		// 将套接字添加到except集合while(1){read_copy = read;except_copy = except;timeout.tv_sec = 5;timeout.tv_usec = 0;result = select(0, &read_copy, 0, &except_copy, &timeout);//开始监视文件描可读数集和与异常集合if(result>0){if(FD_ISSET(hRecvSock, &except_copy)){//使用FD_ISSET宏检查套接字是否在异常集合中。//如果是,表示发生了异常事件,此时会调用recv函数并指定MSG_OOB标志来接收带外数据strLen = recv(hRecvSock, buf, BUFSIZE-1, MSG_OOB);buf[strLen] = 0;printf("urgent message: %s \n", buf);}if(FD_ISSET(hRecvSock, &read_copy)){// 如果检测到读事件strLen = recv(hRecvSock, buf, BUFSIZE-1, 0);if(strLen == 0){break;closesocket(hRecvSock);}else{buf[strLen] = 0;printf("normal message: %s", buf);}}}}closesocket(hAcptSock);closesocket(hRecvSock);WSACleanup();return 0;
}void ErrorHandling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}

在这里插入图片描述
优先接受MSG_OOB信息4和0。

http://www.lryc.cn/news/368009.html

相关文章:

  • Java集合汇总
  • 度小满金融大模型的应用创新
  • Android WebView上传文件/自定义弹窗技术,附件的解决方案
  • selenium 输入框、按钮,输入点击,获取元素属性等简单例子
  • 结构体构造函数
  • 基于单片机的电子万年历设计
  • 大厂真实面试题(一)
  • Docker搭建ELKF日志分析系统
  • 把系统引导做到U盘,实现插上U盘才能开机
  • 【计算机网络基础知识】
  • 个股场外期权个人如何参与买卖?
  • 程序猿大战Python——pycharm软件的使用
  • Unity Standard shader 修改(增加本地坐标裁剪)
  • 【数据结构】排序——插入排序,选择排序
  • 2024.6.9刷题记录
  • Matlab|遗传粒子群-混沌粒子群-基本粒子群
  • 31|HTTP3:甩掉TCP、TLS 的包袱,构建高效网络
  • 2 程序的灵魂—算法-2.2 简单算法举例-【例 2.3】
  • Python中的上下文管理器(contextlib)模块
  • C语言:定义和使用结构体变量
  • Vue3学习第二天记录
  • C语言:双链表
  • Java物业管理系统+数据库应用程序开发[JavaSE+JDBC+idea控制台+MySQL]
  • 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序。.net 读取excel的时候报错(实测有效)
  • JVM垃圾收集器和性能调优
  • 汽车EDI——Volvo EDI 项目案例
  • Qt应用程序发布
  • Python 机器学习 基础 之 【常用机器学习库】 NumPy 数值计算库
  • Linux Kernel nf_tables 本地权限提升漏洞(CVE-2024-1086)
  • [word] word如何清除超链接 #媒体#笔记#知识分享