zoukankan      html  css  js  c++  java
  • Linux网络中接收 "二进制" 流的那些事 --- 就recv的返回值和strlen库函数进行对话

    1.    前言

      很多朋友在做网络编程开发的时候可能都遇到这样的问题,在进行接收二进制流的数据的时候,使用strlen库函数来得到

    二进制数据长度的时候并不准确。为什么呢??首先,使用strlen进行统计长度的为字符串,并非二进制流数据,因此在

    获取二进制数据流的定长中并不适合。解决的问题必然使用网络接收函数的返回值来进行判断,如recv和recvfrom等。

    2.    简单的网络服务器

      Linux中简单的网络服务器做起来很简单,无非就是以下几个步骤

      创建网络套接字(socket) --> 绑定本地套接字到网络中(bind) --> 设置最大监听数目(listen) --> 监听客户端接入(accept)

    3. 具体的例子

    (服务端)

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <errno.h>
     4 #include <sys/types.h>   
     5 #include <sys/socket.h>
     6 #include <arpa/inet.h>
     7 #include <unistd.h>
     8 
     9 #define  WEB_PORT      8080
    10 #define  MAX_CLIENT     5
    11 #define  MAX_RECV      1024
    12 
    13 int main(int argc,char *argv[])
    14 {
    15     // 1. 创建网络套接字
    16     int sock = socket(AF_INET,SOCK_STREAM,0);
    17     if(0 > sock)
    18     {
    19         fprintf(stderr,"socket: %s
    ",strerror(errno));
    20         return -1;
    21     }
    22 
    23     // 2. 设置端口立即释放,可以立即使用
    24     int on = 1;
    25     setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    26 
    27     // 3. 绑定本地套接字到网络中
    28     struct sockaddr_in localAddr;
    29     socklen_t localAddrLen = sizeof(localAddr);
    30 
    31     localAddr.sin_family = AF_INET;
    32     localAddr.sin_port = htons(WEB_PORT);
    33     localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    34 
    35     if(0 > bind(sock,(struct sockaddr *)&localAddr,localAddrLen))
    36     {
    37         fprintf(stderr,"bind: %s
    ",strerror(errno));
    38         return -1;
    39     }
    40 
    41     // 4. 设置最大监听数目
    42     if(0 > listen(sock,MAX_CLIENT))
    43     {
    44         fprintf(stderr,"bind: %s
    ",strerror(errno));
    45         return -1;
    46     }
    47 
    48     // 5. 监听客户端接入
    49     struct sockaddr_in peerAddr;
    50     socklen_t peerAddrLen = sizeof(peerAddr);
    51     char cRecvDataBuf[MAX_RECV] = {0};
    52     ssize_t sRecvRet = 0;
    53 
    54     while(1)
    55     {
    56         int connfd = accept(sock,(struct sockaddr *)&peerAddr,&peerAddrLen);
    57         if(0 > connfd)
    58         {
    59             fprintf(stderr,"accept: %s
    ",strerror(errno));
    60             return -1;    
    61         }
    62 
    63         memset(cRecvDataBuf,0,sizeof(cRecvDataBuf));
    64         sRecvRet = recv(connfd,cRecvDataBuf,sizeof(cRecvDataBuf),0);
    65         if(0 > sRecvRet)
    66         {
    67             fprintf(stderr,"recv: %s
    ",strerror(errno));
    68             return -1;    
    69         }
    70 
    71         printf("
    **************************************
    ");
    72         printf("sRecvRet = %ld
    ",sRecvRet);
    73         printf("strlen(cRecvDataBuf) = %lu
    ",strlen(cRecvDataBuf));
    74         printf("**************************************
    ");
    75 
    76         printf("
    cRecvDataBuf :
    %s
    
    ",cRecvDataBuf);
    77 
    78         close(connfd);
    79     }
    80 
    81     close(sock);
    82 
    83     return 0;
    84 }

    (客户端)

    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>   
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #define  WEB_PORT      8080
    
    int main(int argc,char *argv[])
    {
        // 1. 创建网络套接字
        int sock = socket(AF_INET,SOCK_STREAM,0);
        if(0 > sock)
        {
            fprintf(stderr,"socket: %s
    ",strerror(errno));
            return -1;
        }
    
        // 2. 设置端口立即释放,可以立即使用
        int on = 1;
        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    
        // 3. 设置服务器的地址和链接发送二进制流数据
        struct sockaddr_in serverAddr;
        socklen_t serverAddrLen = sizeof(serverAddr);
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(WEB_PORT);
        serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        if(0 == connect(sock,(struct sockaddr *)&serverAddr,serverAddrLen))
        {
    
            FILE *pFile = fopen("./linux.bin.ub","rb");
            if(NULL != pFile)
            {
                // 4. 获取二进制文件的数据大小            
                fseek(pFile,0,SEEK_END);
                long lFileSize = ftell(pFile);
                rewind(pFile);
    
                // 5. 读取数据并发送
                char *pSendBuf = (char *)malloc(lFileSize+1);
                if(NULL == pSendBuf)
                {
                    fprintf(stderr,"malloc: %s
    ",strerror(errno));    
                    return -1;
                }
    
                memset(pSendBuf,0,lFileSize+1);
                fread(pSendBuf,lFileSize,1,pFile);
                fclose(pFile);
    
                send(sock,pSendBuf,lFileSize,0);
    
                free(pSendBuf);
                close(sock);
            }
    
        }
    
        return 0;
    
    }

    (二进制流数据 : 9.27 MB (9,728,804 字节) )

    4.    比较结果:

      首先本人在服务器端只是接受一次的数据,最大长度为1024字节,那么如何收到的二进制数据的程度实际上应该为1024字节(假设网路正常,只接收一次,由于文件的二进制流数据大小为9728804字节,所以收到的数据长度为1024字节),具体的结果如下:

    5.    结论:

      从结果图可以看出,如果使用strlen进行获取数据的话只有12字节,使用返回值来定长度的话,确实1024字节,这个返回值才是正确的。因

    此,在网络编程中,建议大家在发送二进制文件流或者在接收二进制文件流的时候,切记不要使用strlen进行定长,否则容易出错。但是如果发送的是字符串流的数据的话,这倒不是什么问题,但是为了系统安全和数据的准确性,使用返回值比strlen更加有优

    势。

  • 相关阅读:
    数据库原理分析
    数据库常见索引解析(B树,B-树,B+树,B*树,位图索引,Hash索引)
    数据库索引、B树、B+树
    列存储索引
    比较全面的gdb调试命令
    SQLSERVER如何查看索引缺失
    VIM 实现tab标签页及分屏,切换命令
    查看指定spid的脚本当前运行情况和状态
    通过 sysprocesses 简单查询死锁及解决死锁办法
    ASP.NET Core开源地址
  • 原文地址:https://www.cnblogs.com/Older-Driver-of-Newer/p/7818781.html
Copyright © 2011-2022 走看看