获取socket对应的接收缓冲区中的可读数据量

获取socket对应的接收缓冲区中的可读数据量

本文介绍如何获取当前socket对应的接收缓冲区的可读数据量

在Linux上可以使用ioctl函数

#include <sys/ioctl.h>
		int ioctl (int __fd, unsigned long int __request, ...)

来看一个例子:

#include <sys/types.h> 
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <iostream>
#include <string.h>
#include <vector>
#include <errno.h>

//无效fd标记
#define INVALID_FD  -1

int main(int argc, char* argv[])
{
    //创建一个侦听socket
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == INVALID_FD)
    {
        std::cout << "create listen socket error." << std::endl;
        return -1;
    }
	
	//将侦听socket设置为非阻塞的
	int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);
	int newSocketFlag = oldSocketFlag | O_NONBLOCK;
	if (fcntl(listenfd, F_SETFL,  newSocketFlag) == -1)
	{
		close(listenfd);
		std::cout << "set listenfd to nonblock error." << std::endl;
		return -1;
	}
	
	//复用地址和端口号
	int on = 1;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char *)&on, sizeof(on));
	
	//初始化服务器地址
	struct sockaddr_in bindaddr;
	bindaddr.sin_family = AF_INET;
	bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	bindaddr.sin_port = htons(3000);
	if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)
	{
	    std::cout << "bind listen socket error." << std::endl;
		close(listenfd);
	    return -1;
	}
	
	//启动侦听
	if (listen(listenfd, SOMAXCONN) == -1)
	{
	    std::cout << "listen error." << std::endl;
		close(listenfd);
	    return -1;
	}	
	
	std::vector<pollfd> fds;
	pollfd listen_fd_info;
	listen_fd_info.fd = listenfd;
	listen_fd_info.events = POLLIN;
	listen_fd_info.revents = 0;
	fds.push_back(listen_fd_info);
	
	//是否存在无效的fd标志
	bool exist_invalid_fd;
	int n;
	while (true)
	{
		exist_invalid_fd = false;
		n = poll(&fds[0], fds.size(), 1000);
		if (n < 0)
		{
			//被信号中断
			if (errno == EINTR)
				continue;
			
			//出错,退出
			break;
		}
		else if (n == 0)
		{
			//超时,继续
			continue;
		}
		
		int size = fds.size();
		for (size_t i = 0; i < size; ++i)
		{
			// 事件可读
			if (fds[i].revents & POLLIN)
			{
				if (fds[i].fd == listenfd)
				{
					//侦听socket,接受新连接
					struct sockaddr_in clientaddr;
					socklen_t clientaddrlen = sizeof(clientaddr);
					//接受客户端连接, 并加入到fds集合中
					int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
					if (clientfd != -1)
					{
						//将客户端socket设置为非阻塞的
						int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);
						int newSocketFlag = oldSocketFlag | O_NONBLOCK;
						if (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)
						{
							close(clientfd);
							std::cout << "set clientfd to nonblock error." << std::endl;						
						} 
						else
						{
							struct pollfd client_fd_info;
							client_fd_info.fd = clientfd;
							client_fd_info.events = POLLIN;
							client_fd_info.revents = 0;
							fds.push_back(client_fd_info);
							std::cout << "new client accepted, clientfd: " << clientfd << std::endl;
						}				
					}
				}
				else 
				{
					//socket 可读时获取当前接收缓冲区中的字节数目
					ulong bytesToRecv = 0;
					if (ioctl(fds[i].fd, FIONREAD, &bytesToRecv) == 0)
					{
						std::cout << "bytesToRecv: " << bytesToRecv << std::endl;
					}
					
					//普通clientfd,收取数据
					char buf[64] = { 0 };
					int m = recv(fds[i].fd, buf, 64, 0);
					if (m <= 0)
					{
						if (errno != EINTR && errno != EWOULDBLOCK)
						{
							//出错或对端关闭了连接,关闭对应的clientfd,并设置无效标志位	
							std::cout << "client disconnected, clientfd: " << fds[i].fd << std::endl;
							close(fds[i].fd);
							fds[i].fd = INVALID_FD;
							exist_invalid_fd = true;							
						}			
					}
					else
					{
						std::cout << "recv from client: " << buf << ", clientfd: " << fds[i].fd << std::endl;
					}
				}
			}
			else if (fds[i].revents & POLLERR)
			{
				//TODO: 暂且不处理
			}
			
		}// end  outer-for-loop
		
		if (exist_invalid_fd)
		{
			//统一清理无效的fd
			for (std::vector<pollfd>::iterator iter = fds.begin(); iter != fds.end(); )
			{
				if (iter->fd == INVALID_FD)
					iter = fds.erase(iter);
				else
					++iter;
			}
		}	
	}// end  while-loop
 
	//关闭所有socket
	for (std::vector<pollfd>::iterator iter = fds.begin(); iter != fds.end(); ++ iter)
		close(iter->fd);			
	
	return 0;
}

image-20210707172353351

注意事项:

  1. 对于以下代码,第三个参数bytesToRecv是一个输出参数,对于大多数其他函数意味着bytesToRecv可以不指定初始化值,因为函数调用成功后会为该变量设置值,但对于ioctl函数是个例外,必须将bytesToRecv初始化为0

TCP网络编程的基本流程

Linux与C++11多线程编程(学习笔记)

Linux select函数用法和原理

socket的阻塞模式和非阻塞模式(send和recv函数在阻塞和非阻塞模式下的表现)

connect函数在阻塞和非阻塞模式下的行为

获取socket对应的接收缓冲区中的可读数据量

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值