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\n",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\n",strerror(errno));
38         return -1;
39     }
40 
41     // 4. 设置最大监听数目
42     if(0 > listen(sock,MAX_CLIENT))
43     {
44         fprintf(stderr,"bind: %s\n",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\n",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\n",strerror(errno));
68             return -1;    
69         }
70 
71         printf("\n**************************************\n");
72         printf("sRecvRet = %ld\n",sRecvRet);
73         printf("strlen(cRecvDataBuf) = %lu\n",strlen(cRecvDataBuf));
74         printf("**************************************\n");
75 
76         printf("\ncRecvDataBuf :\n%s\n\n",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\n",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\n",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更加有优

势。

posted on 2017-11-11 12:53  Psrion  阅读(1840)  评论(0编辑  收藏  举报

导航