Linux 网络开发必学课程(六)数据收发的扩展用法

2023-05-16

13数据收发的扩展用法(上)

① 问题

        write()和send()都可以发送数据,有什么区别?

        read()和recv()都可以接收数据,有什么区别? 

② 数据收发选项

        

        调用时flags参数的值为0,表示什么?默认收发网络数据

  • send如果发送缓冲区中有数据,等到被清空,再送到发送缓冲区,send有可能等待。
  • recv等到接收缓冲区有数据,把数据拷贝出来才返回,recv也等待。
  • write   ==> send (….,0)
  • read    ==> recv  (….,0)

③ flags选项信息(常用)

 注意:不同的操作系统对上述可选项的支持不同,实际工程开发时,需要事先对目标系统中支持的可选项进行调研。

④ MSG_OOB(带外数据,紧急数据)

  • 原生定义
    • 使用与普通数据不同的通道独立传输的数据
    • 带外数据优先级比普通数据优先传输,对端优先接收
  • TCP中的带外数据
    • 由于原生设计的限制,TCP无法提供真正意义上带外数据(TCP为流式传输,数据连绵不绝、按序传输)
    • TCP中仅能通过传输协议消息头中的标记,传输紧急数据,且长度仅1字节

⑤ TCP带外数据实现原理

  •  TCP==>包形式传输==>报文
  • URG指针(数据头中)指向紧急消息的下一个位置,即:URG指针
    • 指向位置的前一个字节存储了紧急消息(后发送,先接受)
  • 接收到先接收到0x03,放到一个特殊的缓冲区(1字节)

⑥ TCP带外数据处理策略

  • 由于TCP设计为流式数据,因此,无法做到真正的带外数据
  • 被标记的紧急数据可被提前接收,进入特殊缓冲区(1字节)
    • 每个TCP包最多只有一个紧急数据(1字节)
    • 特殊缓冲区仅存放最近的紧急数据(不及时接收将丢失)
    • 用下面的方式收发数据会发生什么?
      • 发送普通数据,普通方式接收 send (….,0) recv  (….,0) 发什么收什么
      • 发送普通数据紧急方式接收 错误返回紧急方式到特殊缓冲区接收,但是发送是普通数据,所以不会有数据接收
      • 发送紧急数据普通方式接收 紧急方式把数据发送到到特殊缓冲区接收,但是一直在普通缓冲区里收是收不到的,所以recv函数一直在等待….
      • 发送紧急数据紧急方式接收 正常正确收到数据

⑦ 编程实验:TCP紧急数据的发送与接收

client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = 0;
    struct sockaddr_in addr = {0};
    int len = 0;
    char* test = NULL;
    int r = 0;

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if( sock == -1 )
    {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8888);

    if( connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1 )
    {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    test = "Delphi-Tang";

    len = send(sock, test, strlen(test), MSG_OOB);

    getchar();

    close(sock);

    return 0;
}

server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if( server == -1 )
    {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
    {
        printf("server bind error\n");
        return -1;
    }

    if( listen(server, 1) == -1 )
    {
        printf("server bind error\n");
        return -1;
    }

    printf("server start success\n");

    while( 1 )
    {
        asize = sizeof(caddr);

        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if( client == -1 )
        {
            printf("client accept error\n");
            return -1;
        }

        printf("client: %d\n", client);

        do
        {
            r = recv(client, buf, sizeof(buf), MSG_OOB);

            if( r > 0 )
            {
                buf[r] = 0;
                printf("OOB: %s\n", buf);
            }

        } while ( r > 0 );

        close(client);
    }
    
    close(server);

    return 0;
}

select-server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int server_handler(int server)
{
    struct sockaddr_in addr = {0};
    socklen_t asize = sizeof(addr);

    return accept(server, (struct sockaddr*)&addr, &asize);
}

int client_handler(int client)
{
    char buf[32] = {0};
    int ret = recv(client, buf, sizeof(buf)-1, 0);

    if( ret > 0 )
    {
        buf[ret] = 0;

        printf("Receive: %s\n", buf);
    }

    return ret;
}

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int max = 0;
    int num = 0;
    fd_set reads = {0};
    fd_set temps = {0};
    fd_set except = {0};
    struct timeval timeout = {0};

    server = socket(PF_INET, SOCK_STREAM, 0);

    if( server == -1 )
    {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
    {
        printf("server bind error\n");
        return -1;
    }

    if( listen(server, 1) == -1 )
    {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    FD_ZERO(&reads);
    FD_SET(server, &reads);

    max = server;

    while( 1 )
    {
        temps = reads;
        except = reads;

        timeout.tv_sec = 0;
        timeout.tv_usec = 10000;

        num = select(max+1, &temps, 0, &except, &timeout);

        if( num > 0 )
        {
            int i = 0;

            for(i=0; i<=max; i++)
            {
                if( FD_ISSET(i, &except) )
                {
                    if( i != server )
                    {
                        char buf[2]= {0};
                        int r = recv(i, buf, sizeof(buf), MSG_OOB);
                        
                        if( r > 0 )
                        {
                            buf[r] = 0;

                            printf("OOB: %s\n", buf);
                        }
                    }
                }

                if( FD_ISSET(i, &temps) )
                {
                    if( i == server )
                    {
                        int client = server_handler(server);

                        if( client > -1 )
                        {
                            FD_SET(client, &reads);

                            max = (client > max) ? client : max;

                            printf("accept client: %d\n", client); 
                        }
                    }
                    else
                    {
                        int r = client_handler(i);

                        if( r == -1 )
                        {
                            FD_CLR(i, &reads);

                            close(i);
                        }
                    }
                }
            }
        }
    }
    
    close(server);

    return 0;
}

⑧ 小问题:实际开发中,如何高效的接收TCP紧急数据?

⑨ 使用select接收紧急数据

  • socket 上收到普通数据和紧急数据时都会使得select立即返回
    • 普通数据:socket处于数据可读状态(可读取普通数据)
    • 紧急数据:socket处理异常状态(可读取紧急数据)

⑩ 紧急数据接收示例

  编程实验:使用select接收紧急数据

小结论

  • read() / write()可用于收发普通数据(不具备扩展功能)
  • send() / recv()可通过选项信息扩展更多功能
  • TCP紧急数据可标识256种紧急事件(异常事件)
  • 通过select能够及时处理紧急数据,并区分普通数据

14数据收发的扩展用法(下)

MSG_PEEK(数据窥探)

  • 使用MSG_PEEK选项能够获取接收缓冲区数据的拷贝
    • recv()专用选项,可用于数据预接收
    • 指定MSG_PEEK选项时,不会清空缓冲区
    • 可用于获取接收缓冲区中的数据量(字节数) 

② 下面的代码输出什么?为什么?

        

         两次打印的数据相同

MSG_DONTWAIT(立即收发模式)

  • 数据收发时不阻塞,立即返回
    • send() 如果无法将数据送入发送缓冲区,那么直接错误返回
    • recv() 如果接收缓冲区中没有数据,那么直接错误返回
    • send() / recv()返回值
      • -1==>错误发生
      •  0==>对端调用close()关闭连接
      •  n==>发送/接收的数据量

④ 下面的代码输出什么?为什么?

        

空闲时会打印-1和 no data received!

⑤ 再论阻塞发送模式(flags --> 0)

  • send()
    • 发送数据长度>发送缓冲区长度返回错误
    • 发送数据长度<=发送缓冲区剩余长度复制数据到发送缓冲区
    • 发送缓冲区剩余长度<发送数据长度<=发送缓冲区长度等待发送缓冲区清空

⑥ 再论阻塞接收模式(flags --> 0)

  • recv()
    • 接收缓冲区中没有数据时等待数据
    • 接收缓冲区数据量<=接收区长度数据全部拷贝到接收区
    • 接收缓冲区数据量>接收区长度拷贝部分数据到接收区

⑦ 通信框架的迭代增强

 

  • MSG_WAITALL(等待数据)
    • 接收专用,等待需要的数据量完全满足时,recv()才返回
  • MSG_MORE(更多数据)
    • 发送专用,指示内核不着急将发送缓冲区中的数据进行传输

⑧ 下面的代码输出什么?为什么?

         

  • 指示内核不着急将发送缓冲区中的数据进行传输。
  • 等待需要的数据量完全满足时,才发送。

⑨ 编程实验:数据收发扩展用法

client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = 0;
    struct sockaddr_in addr = {0};
    int len = 0;
    char* test = NULL;
    int r = 0;
    int i = 0;

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if( sock == -1 )
    {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8888);

    if( connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1 )
    {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    sleep(1);

    test = "Delphi-Tang";

    for(i=0; i<strlen(test); i++)
    {
        send(sock, test+i, 1, 0);

        if( i % 2 )
        {
            sleep(1);
        }
    }

    test = "quit";

    for(i=0; i<strlen(test)-1; i++)
    {
        send(sock, test+i, 1, MSG_MORE);
        sleep(1);
    }

    send(sock, test+i, 1, 0);

    getchar();

    close(sock);

    return 0;
}

server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if( server == -1 )
    {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
    {
        printf("server bind error\n");
        return -1;
    }

    if( listen(server, 1) == -1 )
    {
        printf("server bind error\n");
        return -1;
    }

    printf("server start success\n");

    while( 1 )
    {
        asize = sizeof(caddr);

        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if( client == -1 )
        {
            printf("client accept error\n");
            return -1;
        }

        printf("client: %d\n", client);

        do
        {
            int len[2] = {11, 4};
            int i = 0;

            for(i=0; i<2; i++)
            {
                r = recv(client, buf, len[i], MSG_WAITALL);

                if( r > 0 )
                {
                    buf[r] = 0;
                    printf("data: %s\n", buf);

                    if( strcmp(buf, "quit") == 0 )
                    {
                        break;
                    }
                }
            }

        } while ( 0 );

        close(client);
    }
    
    close(server);

    return 0;
}

tcp_client.c


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h> 
#include <arpa/inet.h>
#include <unistd.h>
#include <malloc.h>
#include "tcp_client.h"
#include "msg_parser.h"

typedef struct tcp_client
{
    int fd;
    MParser* parser;
    void* data;
} Client;

TcpClient* TcpClient_New()
{
    return TcpClient_From(-1);
}

TcpClient* TcpClient_From(int fd)
{
    Client* ret = malloc(sizeof(Client));

    if( ret )
    {
        ret->fd = fd;
        ret->parser = MParser_New();
        ret->data = NULL;
    }

    return (ret && ret->parser) ? ret : (free(ret), NULL);
}

int TcpClient_SendMsg(TcpClient* client, Message* msg)
{
    int ret = 0;
    Client* c = (Client*)client;

    if( c && msg )
    {
        int len = Message_Size(msg);
        char* data = (char*)Message_H2N(msg);

        ret = (send(c->fd, data, len, 0) != -1);

        Message_N2H(msg);
    }

    return ret;
}

int TcpClient_SendRaw(TcpClient* client, char* buf, int length)
{
    int ret = 0;
    Client* c = (Client*)client;

    if( c && buf )
    {
        ret = send(c->fd, buf, length, 0);
    }

    return ret;
}

Message* TcpClient_RecvMsg(TcpClient* client)
{
    Message* ret = NULL;
    Client* c = (Client*)client;

    if( c  )
    {
        ret = MParser_ReadFd(c->parser, c->fd);
    }

    return ret;
}

int TcpClient_RecvRaw(TcpClient* client, char* buf, int length)
{
    int ret = 0;
    Client* c = (Client*)client;

    if( c && buf )
    {
        ret = recv(c->fd, buf, length, 0);
    }

    return ret;
}

int TcpClient_Connect(TcpClient* client, char* ip, int port)
{
    int ret = TcpClient_IsValid(client);
    Client* c = (Client*)client;

    if( !ret && ip && c && ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) != -1) )
    {
        struct sockaddr_in addr = {0};

        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip);
        addr.sin_port = htons(port);

        ret = (connect(c->fd, (struct sockaddr*)&addr, sizeof(addr)) != -1);
    }

    return ret;
}

int TcpClient_IsValid(TcpClient* client)
{
    int ret = 0;
    Client* c = (Client*)client;

    if( c )
    {
        struct tcp_info info = {0};
        int l = sizeof(info);

        getsockopt(c->fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t*)&l);

        ret = (info.tcpi_state == TCP_ESTABLISHED);
    }

    return ret;
}

void TcpClient_Close(TcpClient* client)
{
    Client* c = (Client*)client;

    if( c )
    {
        close(c->fd);

        c->fd = -1;

        MParser_Reset(c->parser);
    }
}

void TcpClient_Del(TcpClient* client)
{
    Client* c = (Client*)client;

    if( c )
    {
        TcpClient_Close(c);
        MParser_Del(c->parser);
        free(c);
    }
}

void TcpClient_SetData(TcpClient* client, void* data)
{
    Client* c = (Client*)client;

    if( c )
    {
        c->data = data;
    }
}

void* TcpClient_GetData(TcpClient* client)
{
    void* ret = NULL;
    Client* c = (Client*)client;

    if( c )
    {
        ret = c->data;
    }

    return ret;
}

int TcpClient_Available(TcpClient* client)
{
    static char c_temp[1024 * 2] = {0};
    int ret = -1;
    Client* c = (Client*)client;

    if( c )
    {
        ret = recv(c->fd, c_temp, sizeof(c_temp), MSG_PEEK | MSG_DONTWAIT);
    }

    return ret;
}

tcp_client.h

#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H

#include "message.h"

typedef void  TcpClient;

TcpClient* TcpClient_New();
TcpClient* TcpClient_From(int fd);

int TcpClient_SendMsg(TcpClient* client, Message* msg);
int TcpClient_SendRaw(TcpClient* client, char* buf, int length);
Message* TcpClient_RecvMsg(TcpClient* client);
int TcpClient_RecvRaw(TcpClient* client, char* buf, int length);

int TcpClient_Connect(TcpClient* client, char* ip, int port);
int TcpClient_IsValid(TcpClient* client);
void TcpClient_Close(TcpClient* client);
void TcpClient_Del(TcpClient* client);
int TcpClient_Available(TcpClient* client);
void TcpClient_SetData(TcpClient* client, void* data);
void* TcpClient_GetData(TcpClient* client);

#endif

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 网络开发必学课程(六)数据收发的扩展用法 的相关文章

随机推荐