send_recv_writev_readv

本文介绍了send与recv函数在TCP编程中的应用,包括如何发送和接收带外数据,以及如何利用writev与readv函数提高数据通信效率。
部署运行你感兴趣的模型镜像

send与recv

原型:

ssize_t send(int sockfd, const void *buf,size_t nbytes, int flags);

 

ssize_t recv(int sockfd, const void *buf,size_t nbytes, int flags);

 

最后一个参数可取值

MSG_OOB        发送/接收带外数据(out-of-banddata);

MSG_PEEK       返回来的数据并不会在系统内删除,如果再调用,recv()会返回相同的数据内容;

MSG_WAITALL          强迫收到地三个参数所指定的大小的数据后才返回,除非有错误或者信号产生;

MSG_DONTWAIT     调用I/O函数时不阻塞

MSG_NISIGNAL        此操作不愿被SIGPIPE信号中断

 

注:MSG_OOB用于发送”带外数据”紧急消息,实际上TCP不存在真正意义上的”带外数据”,真正意义上的带外数据是通过单独的通信路径告诉传输的消息,但是TCP不另外提供这种通道。只是使用紧急模式传输,其意义在于督促数据接收对象尽快处理数据,TCP的”顺序传输”特性依然成立

 

示例:

服务器端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <signal.h>
#include <fcntl.h>

#define BUF_SIZE 30
void error_handling(const char *message);
void urg_handler(int signo);

int acpt_sock;
int recv_sock;

int main(int argc, const char * argv[]) 
{
    struct sockaddr_in recv_adr, serv_adr;
    int str_len, state;
    socklen_t serv_adr_sz;
    struct sigaction act;
    char buf[BUF_SIZE];
    if( 2 !=argc ) 
	{
        printf("Usage: %s <port> \n", argv[0]);
        exit(1);
    }

    act.sa_handler = urg_handler; //windows下使用select处理,并且OOB数据属于异常
    sigemptyset(&act.sa_mask); 
    act.sa_flags = 0;

    acpt_sock = socket(PF_INET, SOCK_STREAM, 0);
    memset(&recv_adr, 0, sizeof(recv_adr));
    recv_adr.sin_family = AF_INET;
    recv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    recv_adr.sin_port = htons(atoi(argv[1]));

    if( -1 == bind(acpt_sock, (struct sockaddr *) &recv_adr, sizeof(recv_adr)) )
        error_handling("bind() error");
    if( -1 == listen(acpt_sock, 5) )
        error_handling("listen() error");

    serv_adr_sz = sizeof(serv_adr);
    recv_sock = accept(acpt_sock, (struct sockaddr *)&serv_adr, &serv_adr_sz);

    //文件描述符recv_sock所指向的套接字触发的SIGURG信号由进程ID为getpid()的返回值的进程处理,特别是多进程的时候需要设置由谁处理
    fcntl(recv_sock, F_SETOWN, getpid());
    state = sigaction(SIGURG, &act, 0); 	//收到MSG_OOB紧急消息时,操作系统将产生SIGURG,并调用注册的处理函数

    while (0 != (str_len = recv(recv_sock, buf, sizeof(buf), 0)) )//循环接收普通消息
    {
        if( -1 == str_len )
            continue;
        buf[str_len] = 0;
        puts(buf);

    }

    close(recv_sock);
    close(acpt_sock);
    return 0;
}

void urg_handler(int signo)
{
    int str_len;
    char buf[BUF_SIZE];
    str_len = recv(recv_sock, buf, sizeof(buf) - 1, MSG_OOB);	//接收MSG_OOB紧急消息
    buf[str_len] = 0;
    printf("Urgent message : %s \n", buf);
}

void error_handling(const char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

客户端

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(const char *message);

int main(int argc, const char * argv[]) {
    int sock;
    struct sockaddr_in recv_adr;

    if( 3 != argc )
    {
        printf("Usage: %s <IP> <port> \n", argv[0]);
        exit(1);
    }

    sock = socket(PF_INET, SOCK_STREAM, 0);
    if( -1 == sock )
        error_handling("socket() error");
    memset(&recv_adr, 0, sizeof(recv_adr));
    recv_adr.sin_family = AF_INET;
    recv_adr.sin_addr.s_addr = inet_addr(argv[1]);
    recv_adr.sin_port = htons(atoi(argv[2]));

    if( -1 == connect(sock, (struct sockaddr *) &recv_adr, sizeof(recv_adr)) )
        error_handling("connect() error");

    write(sock, "123", strlen("123"));		//发送普通消息
    send(sock, "4", strlen("4"), MSG_OOB);	//发送紧急消息
    write(sock, "567", strlen("567"));
    send(sock, "890", strlen("890"), MSG_OOB);

    close(sock);
    return 0;
}

void error_handling(const char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}


writev与readv

通过函数writev可以将多个buf中的数据整合后写入套接字的输出缓冲区一并发送,调用readv函数可以使用多个buf分别接收输入缓冲区中的数据,因此适当使用这两个函数会减少I/O函数调用次数,而且在没开启Nagle算法时,使用函数writev相比较于使用函数write很可能会减少数据包个数,故使用这两个函数有助于提高数据通信效率

 

writev函数

ssize_t writev(int filedes, const structiovec *iov, int iovcnt);//成功返回发送字节数,失败返回-1

参数一: 数据传递目标的文件描述符,可以是套接字、文件或标准输出描述符

参数二: 指向iovec结构体数组的指针,结构体中包含待发送数据的地址和待发送数据大小

参数三:  第二个参数中结构体数组的长度

 

结构体iovec

struct iovec

{
    void *iov_base; /* Starting address*/

    size_t iov_len; /* Length in bytes */
};

 

示例:

#include <stdio.h>
#include <sys/uio.h>

int main(int argc, const char * argv[]) 
{
    struct iovec vec[2];
    char buf1[] = "ABCDEFG";
    char buf2[] = "1234567";
    int str_len;
    vec[0].iov_base = buf1;	//待传输位置
    vec[0].iov_len = 3;		//带传输大小
    vec[1].iov_base = buf2;
    vec[1].iov_len = 4;

    str_len = writev(1, vec, 2); //1为标准输出
    puts("");
    printf("Write bytes: %d \n", str_len);

    return 0;
}

 

readv函数

ssize_t readv(int filedes, const structiovec *iov, int iovcnt); //成功返回接受字节数,失败返回-1

 

示例:

#include <stdio.h>
#include <sys/uio.h>
#define BUF_SIZE 100

int main(int argc, const char * argv[]) 
{
    struct iovec vec[2];
    char buf1[BUF_SIZE] = { 0 };
    char buf2[BUF_SIZE] = { 0 };
    int str_len;

    vec[0].iov_base = buf1;	//意图存储数据的位置0
    vec[0].iov_len = 5;		//位置0意图存储的最大字节数为5,剩余数据由下一个位置(位置1)存储
    vec[1].iov_base = buf2;	//位置1
    vec[1].iov_len = BUF_SIZE;

    str_len = readv(0, vec, 2);  //0位标准输入
    printf("Read bytes: %d \n", str_len);
    printf("First message: %s \n", buf1);
    printf("Second message: %s \n", buf2);

    return 0;
}


 

 

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

/* *\file server.c *\brief This code is a function for socket tcp server. *\author Chen Xinyi <chenxinyi1@tp-link.com.hk> *\version 1.0.0 *\date 6/8/2025 *\history \arg 1.0.0 6/8/2025, chenxinyi, Create file. */ /**************************************************************************************************/ /*****************************************INCLUDE_FILES********************************************/ /**************************************************************************************************/ #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <errno.h> #include <malloc.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/ioctl.h> #include <stdarg.h> #include <fcntl.h> #include <fcntl.h> /* 涉及到多进程操作时加入如下语句: */ #include <sys/wait.h> #include <sys/ipc.h> #include <sys/shm.h> #include <signal.h> /* 涉及到多线程操作时加入如下语句: */ #include <pthread.h> #include <sys/poll.h> /**************************************************************************************************/ /********************************************DEFINES***********************************************/ /**************************************************************************************************/ #define PORT 80 /**************************************************************************************************/ /********************************************FUNCTION**********************************************/ /**************************************************************************************************/ int main() { /* 1.创建socket文件描述符:创建套接字成功时返回一个非负整数 */ int server_fd; if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } /* 声明serv_addr变量, sockaddr_in定义在头文件<arpa/inet.h>中,专用于互联网环境下的套接字地址格式 */ /* struct sockaddr_in { short int sin_family; 地址族 unsigned short int sin_port;端口号 struct in_addr sin_addr; Internet地址 unsigned char sin_zero[8]; 与struct sockaddr一样的长度 }; */ /* 2.配置服务器地址 */ struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); /* 使用IPV4地址 */ serv_addr.sin_family = AF_INET; /* 设置IP,监听本地所有网口,仅服务端可用INADDR_ANY */ serv_addr.sin_addr.s_addr = INADDR_ANY; /* 端口!需要转化为网络字节序再赋给socket */ serv_addr.sin_port = htons(PORT); /* 3.绑定server_fd到端口,文件描述符绑定到指定地址和端口 */ /* bind(socket描述字,要绑定给描述字的协议地址,对应地址长度)函数把一个地址组中特定地址赋给socket */ /* 绑定成功之后,如果客户端试图连接指定地址和端口建立TCP连接,系统就会转给server_fd这个文件处理 */ if (bind(server_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } /* 4.进入监听状态 如果客户端用connct()发出请求,服务器就会监听到 */ /* int listen(int sockfd, int backlog) 监听的socket描述字,相应socket可以排队的最大连接个数 */ /* 监听server_fd,就是鉴定这个地址和端口 */ if (listen(server_fd, 3) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } printf("TCP服务器已启动, 监听端口: %d\n", PORT); /* 5.接受客户端连接请求 */ /* 返回客户端的协议地址,server_fd就是用于监听的,client_fd采用来进行实际的数据传输 */ struct sockaddr_in client_addr; /* 协议地址的长度,socklen_t用于表示套接字地址结构长度的专用数据类型,在 <sys/socket.h> 头文件中定义 */ socklen_t clnt_addr_size = sizeof(client_addr); /* int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); */ /* 服务器的socket描述字, 指向struct sockaddr *的指针, 协议地址的长度指针 */ int clnt_sock = accept(server_fd, (struct sockaddr*)&client_addr, &clnt_addr_size); if (clnt_sock < 0) { perror("accept failed"); exit(EXIT_FAILURE); } printf("客户端已连接: %s\n", inet_ntoa(serv_addr.sin_addr)); /* 向客户端发送数据 */ char str[] = "hello world!"; /* 网络I/O操作read()/write();recv()/send();readv()/writev(); recvmsg()/sendmsg();recvfrom()/sendto() */ write(clnt_sock, str, sizeof(str)); /* 关闭套接字 */ close(clnt_sock); close(server_fd); return 0; } 这个代码中client_addr,serv_addr能否都写作serv_addr?这份别的含义是什么
最新发布
08-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值