MSG_PEEK标志可以用来读取套接字接收队列中可读的数据,一些情况会用到它,比如为了避免不阻塞而先检查套接字接收队列中可读的数据长度,再采取相应操作。

当然,不阻塞也可采取其他的方法,例如非阻塞式I/O。


MSG_PEEK标志会将套接字接收队列中的可读的数据拷贝到缓冲区,但不会使套接子接收队列中的数据减少,常见的是:例如调用recv或read后,导致套接字接收队列中的数据被读取后而减少,而指定了MSG_PEEK标志,可通过返回值获得可读数据长度,并且不会减少套接字接收缓冲区中的数据,所以可以供程序的其他部分继续读取。


注意:假设指定MSG_PEEK标志,以一个长度为1024字节的缓冲区对一个TCP套接字调用recv,返回100,如果再次调用recv,返回值可能超过100,因为两次调用之间可能有新的数据到达,导致长度增加。




下面是一个客户-服务程序,客户向服务端发送"Hello Server",服务器端指定MSG_PEEK标志获得可读的长度后,并再次调用不指定MSG_PEEK的recv读取,两次读取都能得到数据,因为指定了MSG_PEEK后套接字接收缓冲区不会减少。

net.h



    1. #ifndef MY_NET_H    
    2. #define MY_NET_H    
    3.         
    4. #include <sys/types.h>          
    5. #include <sys/socket.h>    
    6. #include <stdio.h>    
    7. #include <stdlib.h>    
    8. #include <arpa/inet.h>    
    9. #include <unistd.h>    
    10. #include <time.h>    
    11. #include <string.h>    
    12. #include <sys/select.h>    
    13. #include <sys/time.h>    
    14. #include <errno.h>  
    15. #include <signal.h>  
    16. #include <iostream>  
    17. #include <sys/stat.h>  
    18. #include <fcntl.h>  
    19.   
    20. using namespace std;  
    21.         
    22. #define SA struct sockaddr  
    23.     
    24. /*
    25. *Init_sockaddr  初始化地址结构
    26. *stru   指向地址结构的指针
    27. *protoc 要采用的地址族
    28. *addr   ip地址,不能为INADDR_ANY
    29. *port   端口号
    30. *返回值:成功返回0,出错返回-1
    31. *提示:不对protoc(地址族)参数进行检查
    32. */  
    33. int Init_sockaddr(struct sockaddr_in *stru, const int protoc, const char *addr,const unsigned int port)  
    34. {  
    35. if (stru == NULL || addr == NULL)  
    36. return -1;  
    37.       
    38. /*ip地址格式不正确*/  
    39. if (inet_addr(addr) == INADDR_NONE)  
    40. return -1;  
    41.           
    42. /*端口超出65535*/  
    43. if (port > 65535)  
    44. return -1;  
    45.       
    46. void*)stru, sizeof(*stru));  
    47.     stru->sin_family = protoc;  
    48.     (stru->sin_addr).s_addr = inet_addr(addr);  
    49.     stru->sin_port = htons(port);  
    50.       
    51. return 0;  
    52. }  
    53.   
    54. /*
    55. *tcp_connect 建立一个TCP套接字并连接到指定ip地址的指定端口(阻塞版本,connect会一直阻塞,直到到达默认超时时间)
    56. *addr ip地址
    57. *port 端口
    58. *返回值:连接成功返回描述符,出错返回-1
    59. */  
    60. int Tcp_connect(const char *addr,const unsigned int port)  
    61. {     
    62. int sockfd;  
    63. struct sockaddr_in saddr;  
    64.       
    65. /*参数不合法*/     
    66. if((Init_sockaddr(&saddr, AF_INET, addr, port)) == -1)  
    67. return -1;  
    68.           
    69. /*socket异常*/  
    70. if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
    71. return -1;  
    72.       
    73. /*连接不成功或超时*/  
    74. if(connect(sockfd, (SA*)&saddr, sizeof(saddr)) == -1)  
    75.     {  
    76.         close(sockfd);  
    77. return -1;  
    78.     }  
    79.           
    80. return sockfd;  
    81. }  
    82.   
    83. /*
    84. *tcp_listen 建立一个套接字,并且绑定,监听
    85. *addr 要绑定的ip地址 INADDR_ANY或ipv4地址
    86. *port 要监听的端口
    87. *backlog listen函数的监听排队数
    88. *返回值:成功返回套接字描述符,出错返回-1
    89. */  
    90. int Tcp_listen(const char *addr,const unsigned int port,const int backlog)  
    91. {  
    92. int sockfd;  
    93. struct sockaddr_in saddr;  
    94.       
    95. if (addr == NULL)  
    96. return -1;  
    97.       
    98. if (strcmp(addr, "INADDR_ANY") == 0)  
    99.     {  
    100. /*端口超出65535*/  
    101. if (port > 65535)  
    102. return -1;  
    103.           
    104. /*排队数不合法*/  
    105. if (backlog < 0)  
    106. return -1;  
    107.           
    108. void*)&saddr, sizeof(saddr));  
    109.         saddr.sin_family = AF_INET;  
    110.         saddr.sin_addr.s_addr = htonl(INADDR_ANY);  
    111.         saddr.sin_port = htons(port);  
    112.     }  
    113. else  
    114.     {  
    115. /*参数不合法*/  
    116. if (Init_sockaddr(&saddr, AF_INET, addr, port) == -1)  
    117. return -1;  
    118.     }  
    119.       
    120. /*socket异常*/  
    121. if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
    122. return -1;  
    123.       
    124. /*bind*/  
    125. if (bind(sockfd, (SA*)&saddr, sizeof(saddr)) == -1)  
    126.     {  
    127.         close(sockfd);  
    128. return -1;  
    129.     }  
    130.       
    131. /*listen*/  
    132. if (listen(sockfd, backlog) == -1)  
    133.     {  
    134.         close(sockfd);  
    135. return -1;  
    136.     }  
    137.       
    138. return sockfd;  
    139. }  
    140.   
    141. #endif



    客户程序



      1. #include <iostream>  
      2. #include "net.h"  
      3. using namespace std;  
      4.   
      5. int main()  
      6. {  
      7. int sockfd;  
      8. "127.0.0.1", 9999);  
      9. if (sockfd == -1)  
      10.     {  
      11. "Tcp_connect error" << endl;  
      12. return -1;  
      13.     }  
      14. char send_buf[] = "Hello Server";  
      15. char *p = send_buf;  
      16. int r;  
      17. int count = 0;  
      18. while (1)  
      19.     {  
      20.         r = write(sockfd, p, strlen(p));  
      21. if (r == -1)  
      22.         {  
      23. "write error");  
      24. return -1;  
      25.         }  
      26.         p += r;  
      27.         count += r;  
      28. if (count == strlen(send_buf))  
      29. break;  
      30.     }  
      31. while(1);  
      32. return 0;  
      33. }



      服务器程序



        1. #include <iostream>  
        2. #include <unistd.h>  
        3. #include "net.h"  
        4. using namespace std;  
        5.   
        6. int main()  
        7. {  
        8. int sockfd;  
        9. "INADDR_ANY", 9999, 5);  
        10. if (sockfd == -1)  
        11.     {  
        12. "Tcp_listen error" << endl;  
        13. return -1;  
        14.     }  
        15.       
        16. int clifd;  
        17. if ((clifd = accept(sockfd, NULL, NULL)) == -1)  
        18.     {  
        19. "accept error" << endl;  
        20. return -1;  
        21.     }  
        22. "有新连接" << endl;  
        23.       
        24. //确保客户端有数据发送到服务端(本地测试可行)  
        25.     sleep(5);  
        26.       
        27. char buf[100];  
        28. int r;  
        29. //利用MSG_PEEK标志读取套接子接收队列中可读的数据长度,  
        30. sizeof(buf), MSG_PEEK);  
        31. "接收队列中可读的数据长度:" << r << endl;//此处"Hello Server"的长度为12,由于采用tcp,不一定所有数据都会到达服务端,所以值应<=12  
        32. "buf:" << buf << endl;  
        33. sizeof(buf), 0);  
        34. "读取长度:" << r << endl;             //该长度可能会大于上一个recv返回的长度,因为在此期间可能又有来自客户的数据到达  
        35. "buf:" << buf << endl;  
        36. return 0;  
        37. }



        执行结果


        recv_MSG_peek_#include