Linux網絡中接收 "二進制" 流的那些事 --- 就recv的返回值和strlen庫函數進行對話

1.    前言linux

  不少朋友在作網絡編程開發的時候可能都遇到這樣的問題,在進行接收二進制流的數據的時候,使用strlen庫函數來獲得編程

二進制數據長度的時候並不許確。爲何呢??首先,使用strlen進行統計長度的爲字符串,並不是二進制流數據,所以在安全

獲取二進制數據流的定長中並不適合。解決的問題必然使用網絡接收函數的返回值來進行判斷,如recv和recvfrom等。服務器

2.    簡單的網絡服務器網絡

  Linux中簡單的網絡服務器作起來很簡單,無非就是如下幾個步驟併發

  建立網絡套接字(socket) --> 綁定本地套接字到網絡中(bind) --> 設置最大監聽數目(listen) --> 監聽客戶端接入(accept)socket

3. 具體的例子函數

(服務端)spa

 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 }

(客戶端)3d

#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更加有優

勢。

相關文章
相關標籤/搜索