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. }
执行结果