获取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  -1int main(int argc, char* argv[])
{//创建一个侦听socketint 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-loopif (exist_invalid_fd){//统一清理无效的fdfor (std::vector<pollfd>::iterator iter = fds.begin(); iter != fds.end(); ){if (iter->fd == INVALID_FD)iter = fds.erase(iter);else++iter;}}	}// end  while-loop//关闭所有socketfor (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对应的接收缓冲区中的可读数据量

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux epoll的用法

Linux epoll的用法 epollfd_create函数 #include <sys/epoll.h>int epoll_create (int __size)参数含义__size此参数从Linux 2.6.8后就不再使用了,但必须设置成大于零的值 返回值含义>0可用的epollfd-1调用失败 epollfd_ctl函数 有了epollfd,我们需要将要检测事件…

windows网络编程

windows网络编程 TCP编程 服务端 这里我们有几点需要注意: 使用WSAStartup初始化网络库,即将与socket函数相关dll文件加载到进程地址空间中退出时,使用WSACleanup()卸载相关dll文件与Linux使用close函数关闭socket不同,windows需要使用closesocket函数关闭socket WSAStart…

TCP服务器epoll的多种实现

TCP服务器epoll的多种实现 对于网络IO会涉及到两个系统对象 用户空间中进程或者线程操作系统内核 比如发生read操作时就会经历两个阶段 等待数据就绪将数据从内核缓冲区拷贝到用户缓冲区 由于各个阶段多有不同的情况,一组合么就产生了多种网络 IO 模型 阻塞IO 在Linux中…

侯捷面向对象高级编程(二)

侯捷面向对象高级编程(二) 转换函数 转换函数没有返回值,返回值就是double即函数名,不需要自己写因为转换函数一般不会改变其中内容,所以要加const限定 两条路都可以走,就回产生歧义,报错 explict禁止自动转换,于是4无法转转换为Fraction pointer-like cliasses ->作用之后…

拒绝了对对象 'sp_sdidebug'(数据库 'master',所有者 'dbo')的 EXECUTE 权限

在.net中调用时出现“拒绝了对对象 sp_sdidebug&#xff08;数据库 master&#xff0c;所有者 dbo&#xff09;的 EXECUTE 权限”的错误的解决办法。该问题是我在用指定的URL启动项目后&#xff0c;再“附加进程”后运行程序时出现的。该问题主要是.net2005的调试机制引起的&am…

ASP.NET MVC 音乐商店 - 6. 使用 DataAnnotations 进行模型验证

在前面的创建专辑与编辑专辑的表单中存在一个问题&#xff1a;我们没有进行任何验证。字段的内容可以不输入&#xff0c;或者在价格的字段中输入一些字符&#xff0c;在执行程序的时候&#xff0c;这些错误会导致数据库保存过程中出现错误&#xff0c;我们将会看到来自数据库的…

EF Code First学习笔记:数据库创建(转)

控制数据库的位置 默认情况下&#xff0c;数据库是创建在localhost\SQLEXPRESS服务器上&#xff0c;并且默认的数据库名为命名空间context类名&#xff0c;例如我们前面的BreakAway.BreakAwayContext。 有几种方法可以改变这种默认约定。 利用配置文件 在配置文件中新加一个连接…

ASP.NET 2.0+Atlas编写鼠标拖放程序

作者&#xff1a;朱先忠编译摘要 本文将详细探讨Atlas中的声明性编程与强制性编程之间的关系&#xff0c;及如何用之在一个web客户端实现拖放功能。下图为本文相应示例程序运行结果快照。运行结果一. 简介  本文旨在帮助读者理 解微软的Atlas技术的某些方面的工作原理。Atlas…

C++11异步操作

C11异步操作 C 11 提供了异步操作相关的类,主要有std::future std::promise std::package_task std::future作为异步结果的传输通道,获取线程函数的返回值; std::promise用来包装一个值,将数据和std::future绑定; std::package用来包装一个对象,将数据和future绑定起来,以方…