MSG_PEEK标志

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

#ifndef MY_NET_H  
#define MY_NET_H  
      
#include <sys/types.h>        
#include <sys/socket.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <time.h>  
#include <string.h>  
#include <sys/select.h>  
#include <sys/time.h>  
#include <errno.h>
#include <signal.h>
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;
      
#define SA struct sockaddr
  
/*
*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;
}

#endif

客户程序

#include <iostream>
#include "net.h"
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;
}

服务器程序

#include <iostream>
#include <unistd.h>
#include "net.h"
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;
}

执行结果



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值