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


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段代码主要是针对一个串口通信模块进行初始化操作,包括创建发送消息队列和接收任务。下面是代码的详细解释: 1. `if(NULL == g_my_uart[uart].m_send_queue_handle)`:判断发送消息队列是否已经被创建,如果没有被创建则执行下面的代码。 2. `g_my_uart[uart].m_send_queue_handle = xQueueCreate(UART_SEND_QUEUE_LEN,sizeof(uart_send_msg));`:创建一个长度为UART_SEND_QUEUE_LEN,每个元素大小为uart_send_msg的队列,并将其句柄保存到g_my_uart[uart].m_send_queue_handle变量中。 3. `if(NULL == g_my_uart[uart].m_send_queue_handle)`:判断队列是否创建成功,如果没有成功则执行下面的代码。 4. `log_error("Queue creat filed.");`:打印错误信息。 5. `vPortFree(g_my_uart[uart].m_recv_buf); g_my_uart[uart].m_recv_buf = NULL;`:释放接收缓冲区的内存,并将指针置为NULL。 6. `return E_ERROR;`:返回错误码E_ERROR,表示函数执行失败。 7. `if(NULL == g_my_uart[uart].m_recv_task_handle)`:判断接收任务是否已经被创建,如果没有被创建则执行下面的代码。 8. `os_ret = xTaskCreate((TaskFunction_t )m_recv_task, (const char* )g_my_uart_cfg[uart].m_recv_task_name, (uint16_t )g_my_uart_cfg[uart].m_recv_task_stk, (void* )&g_my_uart[uart], (UBaseType_t )g_my_uart_cfg[uart].m_recv_task_pro, (TaskHandle_t* )&g_my_uart[uart].m_recv_task_handle);`:创建一个名为m_recv_task的任务,并将其句柄保存到g_my_uart[uart].m_recv_task_handle变量中。 9. `if(pdPASS != os_ret)`:判断任务是否创建成功,如果没有成功则执行下面的代码。 10. `log_error("UART recv task creat filed,ret=%d",(int)os_ret);`:打印错误信息。 11. `vPortFree(g_my_uart[uart].m_recv_buf); g_my_uart[uart].m_recv_buf = NULL;`:释放接收缓冲区的内存,并将指针置为NULL。 12. `return E_ERROR;`:返回错误码E_ERROR,表示函数执行失败。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值