MSG_PEEK标志

https://blog.csdn.net/aspnet_lyc/article/details/28937229

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

客户程序

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

服务器程序

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

执行结果




猜你喜欢

转载自blog.csdn.net/sinat_35297665/article/details/81038167