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

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

1.readvwritev函数

1.1 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; //缓冲区的长度,即需要传输的字节数
};

1.2 readv()函数示例代码

#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;
}

在这里插入图片描述

1.3 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; //缓冲区的长度,即需要传输的字节数
};

1.4 writev()函数示例代码

#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()监视下会被视为异常数据

2.1 示例代码

发送端代码

#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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青石横刀策马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值