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
- using namespace std;
- /*
- *Init_sockaddr 初始化地址結構
- *stru 指向地址結構的指針
- *protoc 要采用的地址族
- *addr ip地址,不能為INADDR_ANY
- *port 端口號
- *返回值:成功返回0,出錯返回-1
- *提示:不對protoc(地址族)參數進行檢查
- */
- int Init_sockaddr(struct sockaddr_in *stru, const int protoc, const char *addr,const unsigned int port)
- {
- if (stru == NULL || addr == NULL)
- return -1;
- /*ip地址格式不正確*/
- if (inet_addr(addr) == INADDR_NONE)
- return -1;
- /*端口超出65535*/
- if (port > 65535)
- return -1;
- bzero((void*)stru, sizeof(*stru));
- stru->sin_family = protoc;
- (stru->sin_addr).s_addr = inet_addr(addr);
- stru->sin_port = htons(port);
- return 0;
- }
- /*
- *tcp_connect 建立一個TCP套接字并連接到指定ip地址的指定端口(阻塞版本,connect會一直阻塞,直到到達默認超時時間)
- *addr ip地址
- *port 端口
- *返回值:連接成功返回描述符,出錯返回-1
- */
- int Tcp_connect(const char *addr,const unsigned int port)
- {
- int sockfd;
- struct sockaddr_in saddr;
- /*參數不合法*/
- if((Init_sockaddr(&saddr, AF_INET, addr, port)) == -1)
- return -1;
- /*socket異常*/
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- return -1;
- /*連接不成功或超時*/
- if(connect(sockfd, (SA*)&saddr, sizeof(saddr)) == -1)
- {
- close(sockfd);
- return -1;
- }
- return sockfd;
- }
- /*
- *tcp_listen 建立一個套接字,并且綁定,監聽
- *addr 要綁定的ip地址 INADDR_ANY或ipv4地址
- *port 要監聽的端口
- *backlog listen函數的監聽排隊數
- *返回值:成功返回套接字描述符,出錯返回-1
- */
- int Tcp_listen(const char *addr,const unsigned int port,const int backlog)
- {
- int sockfd;
- struct sockaddr_in saddr;
- if (addr == NULL)
- return -1;
- if (strcmp(addr, "INADDR_ANY") == 0)
- {
- /*端口超出65535*/
- if (port > 65535)
- return -1;
- /*排隊數不合法*/
- if (backlog < 0)
- return -1;
- bzero((void*)&saddr, sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_addr.s_addr = htonl(INADDR_ANY);
- saddr.sin_port = htons(port);
- }
- else
- {
- /*參數不合法*/
- if (Init_sockaddr(&saddr, AF_INET, addr, port) == -1)
- return -1;
- }
- /*socket異常*/
- if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
- return -1;
- /*bind*/
- if (bind(sockfd, (SA*)&saddr, sizeof(saddr)) == -1)
- {
- close(sockfd);
- return -1;
- }
- /*listen*/
- if (listen(sockfd, backlog) == -1)
- {
- close(sockfd);
- return -1;
- }
- return sockfd;
- }
客戶程序
- using namespace std;
- int main()
- {
- int sockfd;
- sockfd = Tcp_connect("127.0.0.1", 9999);
- if (sockfd == -1)
- {
- cout << "Tcp_connect error" << endl;
- return -1;
- }
- char send_buf[] = "Hello Server";
- char *p = send_buf;
- int r;
- int count = 0;
- while (1)
- {
- r = write(sockfd, p, strlen(p));
- if (r == -1)
- {
- perror("write error");
- return -1;
- }
- p += r;
- count += r;
- if (count == strlen(send_buf))
- break;
- }
- while(1);
- return 0;
- }
服務器程序
- using namespace std;
- int main()
- {
- int sockfd;
- sockfd = Tcp_listen("INADDR_ANY", 9999, 5);
- if (sockfd == -1)
- {
- cout << "Tcp_listen error" << endl;
- return -1;
- }
- int clifd;
- if ((clifd = accept(sockfd, NULL, NULL)) == -1)
- {
- cout << "accept error" << endl;
- return -1;
- }
- cout << "有新連接" << endl;
- //確保客戶端有數據發送到服務端(本地測試可行)
- sleep(5);
- char buf[100];
- int r;
- //利用MSG_PEEK標志讀取套接子接收隊列中可讀的數據長度,
- r = recv(clifd, buf, sizeof(buf), MSG_PEEK);
- cout << "接收隊列中可讀的數據長度:" << r << endl;//此處"Hello Server"的長度為12,由于采用tcp,不一定所有數據都會到達服務端,所以值應<=12
- cout << "buf:" << buf << endl;
- r = recv(clifd, buf, sizeof(buf), 0);
- cout << "讀取長度:" << r << endl; //該長度可能會大于上一個recv返回的長度,因為在此期間可能又有來自客戶的數據到達
- cout << "buf:" << buf << endl;
- return 0;
- }
執行結果