如何偷窥到socket对应的内核缓冲区中有什么数据? 有多少数据?—利用recv的MSG_PEEK和ioctlsocket的FIONREAD

如何偷窥到socket对应的内核缓冲区中有什么数据? 有多少数据?—利用recv的MSG_PEEK和ioctlsocket的FIONREAD很多时候,应用程序仅仅想知道内核缓冲区中有什么数据,或者想知道有多少数据可读,也就是说,应用程序仅仅想偷窥一下里面的数据,并不是想偷取,那怎么办呢?事实上,我们之前已经大致说过,现在,我们继续来复习一下recv的MSG_PEEK:服务端程序为:#include#include//winsock接口#pragmacomm

大家好,又见面了,我是你们的朋友全栈君。

       很多时候, 应用程序仅仅想知道内核缓冲区中有什么数据, 或者想知道有多少数据可读, 也就是说, 应用程序仅仅想偷窥一下里面的数据, 并不是想偷取, 那怎么办呢?

 

       事实上, 我们之前已经大致说过, 现在, 我们继续来复习一下recv的MSG_PEEK:

       服务端程序为:

 

#include <stdio.h>
#include <winsock2.h> // winsock接口
#pragma comment(lib, "ws2_32.lib") // winsock实现

int main()
{
	WORD wVersionRequested;  // 双字节,winsock库的版本
	WSADATA wsaData;         // winsock库版本的相关信息
	
	wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257
	

	// 加载winsock库并确定winsock版本,系统会把数据填入wsaData中
	WSAStartup( wVersionRequested, &wsaData );
	

	// AF_INET 表示采用TCP/IP协议族
	// SOCK_STREAM 表示采用TCP协议
	// 0是通常的默认情况
	unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;

	addrSrv.sin_family = AF_INET; // TCP/IP协议族
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // socket对应的IP地址
	addrSrv.sin_port = htons(8888); // socket对应的端口

	// 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程)
	bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	// 将socket设置为监听模式,5表示等待连接队列的最大长度
	listen(sockSrv, 5);

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);

	while(1)
	{
		// sockSrv为监听状态下的socket
		// &addrClient是缓冲区地址,保存了客户端的IP和端口等信息
		// len是包含地址信息的长度
		// 如果客户端没有启动,那么程序一直停留在该函数处
		unsigned int sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len);
		
		while(1)
		{
			getchar();

			char sendBuf[100] = "good";
			send(sockConn, sendBuf, strlen(sendBuf), 0); // 每次发送5个字节过去
		}
	}
		
	closesocket(sockSrv);
	WSACleanup();
	
	return 0;
}

      开启服务端。

 

 

      客户端程序为:

 

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

int main()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(1, 1);
	
	WSAStartup( wVersionRequested, &wsaData );

	SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(8888);
	connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	while(1)
	{
        char recvBuf[100] = {0};  
        recv(sockClient, recvBuf, 100 - 1, MSG_PEEK);  // 注意: 最后的参数是MSG_PEEK, 而不是0

        //printf("%s\n", recvBuf);  // 不要用这个, 否则打印不全,要用下面的:
		int i = 0;
		for(i = 0; i < 99; i++)
		{
			putchar(recvBuf[i]);
		}
		
		getchar();
	}

	closesocket(sockClient);
	WSACleanup();

	return 0;
}

     开启客户端。

 

 

     我们看到, 当服务端不停地给客户端发送数据的时候, 客户端的recv并不会把数据从内核缓冲区中取出来, 此时, 内核缓冲区中的数据不断累积, 每次累积5个字节。 在实际实验中, 我发了3次, 客户端接收3次,客户端的结果如下:

good  (第1次偷窥到的)

good good (第2次偷窥到的)

good good good (第3次偷窥到的)

      可以看到, 数据在内核缓冲区确实是不断积累的, 也从侧面证明了, recv并只是在偷窥, 而非偷取。 好, 我们关掉服务端和客户端, 不要影响后面的实验。

 

      继续讨论, 有的时候, 我们不是想看里面有什么数据, 而是想知道里面有多少数据(可能是为了便于知道随后该读取多少), 其实, 用recv的MSG_PEEK也是可以做到的, 但更好的方法是, 直接利用ioctlsocket的FIONREAD去获取。 我查阅了很多资料, 却不知道FIONREAD中这个N的对应的具体单词, 那我就斗胆猜测是not, 也就是说, FIONREAD是function, input, output, not, read的缩写, 从字面意思看, 它不会去read(此处指代偷取)出来内核缓冲区里面的数据。 该看程序了:

      服务端程序依然为:

 

#include <stdio.h>
#include <winsock2.h> // winsock接口
#pragma comment(lib, "ws2_32.lib") // winsock实现

int main()
{
	WORD wVersionRequested;  // 双字节,winsock库的版本
	WSADATA wsaData;         // winsock库版本的相关信息
	
	wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257
	

	// 加载winsock库并确定winsock版本,系统会把数据填入wsaData中
	WSAStartup( wVersionRequested, &wsaData );
	

	// AF_INET 表示采用TCP/IP协议族
	// SOCK_STREAM 表示采用TCP协议
	// 0是通常的默认情况
	unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;

	addrSrv.sin_family = AF_INET; // TCP/IP协议族
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // socket对应的IP地址
	addrSrv.sin_port = htons(8888); // socket对应的端口

	// 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程)
	bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	// 将socket设置为监听模式,5表示等待连接队列的最大长度
	listen(sockSrv, 5);

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);

	while(1)
	{
		// sockSrv为监听状态下的socket
		// &addrClient是缓冲区地址,保存了客户端的IP和端口等信息
		// len是包含地址信息的长度
		// 如果客户端没有启动,那么程序一直停留在该函数处
		unsigned int sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len);
		
		while(1)
		{
			getchar();

			char sendBuf[100] = "good";
			send(sockConn, sendBuf, strlen(sendBuf), 0); // 发送数据到客户端,最后一个参数一般设置为0
		}
	}
		
	closesocket(sockSrv);
	WSACleanup();
	
	return 0;
}

     开启服务端:

 

    

     再看客户端, 程序为:

 

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

int main()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(1, 1);
	
	WSAStartup( wVersionRequested, &wsaData );

	SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(8888);
	connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	while(1)
	{
		getchar();
		unsigned long bytes = 0;
		ioctlsocket(sockClient, FIONREAD, &bytes); // 探测内核缓冲区中有多少数据可以接收
		printf("%d\n", bytes);
	}

	closesocket(sockClient);
	WSACleanup();

	return 0;
}

      开启它。

 

 

      我们同样让服务端端依次发3次数据, 发第一次后, 客户端探测, 发现有5个字节, 发第二次, 客户端再探测, 发现有10个字节, 发第三次, 客户端再次探测, 发现有15个字节, 可见, 正确探测到了内核缓冲区待接收的数据的多少。 客户端结果为:

5

10

15

 

     以上两种方法在利用tcp keepalive进行断网检测中经常用到, 后面我会继续介绍tcp keepalive的断网检测。 

 

     ok, 本文就到此为止。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/163628.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java中什么是过滤器_JAVAweb过滤器

    java中什么是过滤器_JAVAweb过滤器【扩展】过滤器:Filter概念:对目标资源的请求和响应进行过滤截取。在请求到达servlet之前,进行逻辑判断,判断是否放行到servlet;也可以在一个响应response到达客户端之前进行过滤,判断是否允许返回客户端。场景:(用户授权的过滤器:判断用户是否有权限请求界面)(日志信息的过滤器:过滤用户在网站的所有请求,记录轨迹 )(负责解码的过滤器:规定请求的解码方式)备注:过滤…

    2022年8月23日
    5
  • 什么是RIA 技术?

    什么是RIA 技术?RIA是什么东西,这是真么技术。。。我写这篇博客中,就带着这么两个问题开始收集资料RIA(RichInterfaceApplications)富界面应用,俗称胖客户端.RIA的优势RIA具有的

    2022年7月4日
    24
  • Loadlibrary 失败的原因

    Loadlibrary 失败的原因  今天调试公司的游戏程序时,发现Loadlibrary总是调用失败,查看加载的动态库路径完全正确,为什么还是会加载失败呢?莫非是这个被加载的动态库本身有问题,我用工具查看了其依赖性,发现没问题,难道是该动态库依赖的动态库有问题?我继续查看该动态库依赖的动态库依赖性果然是这样。

    2022年7月13日
    17
  • tl494cn逆变器电路图_用TL494制作的逆变电源[通俗易懂]

    tl494cn逆变器电路图_用TL494制作的逆变电源[通俗易懂]TL494集成块广泛应用在开关电源,其内部集成有PWM、三角波发生器、电池欠压检测,+5V电压基准等电路,具有外接元件少,控制稳定的特点。笔者在网上查阅大量资料,自制了一款准正弦波300W逆变器,采用直流12V电瓶供电,可供小功率单相电机、日光灯等电感性负载用电,电路如附图所示。该逆变板工作频率由TL494⑤、⑥脚外接阻容元件确定,本例为2.2kHz左右。该频率的大小直接影响功率场效应管的功率损耗…

    2022年6月3日
    157
  • app测试工具monkey_monkeyapp下载

    app测试工具monkey_monkeyapp下载前言Monkey是Android中的一个命令行工具,可以运行在模拟器里或实际设备中。接下来将讲解如何用真机来测试Androidapp操作步骤手机连接电脑(用数据线进行连接)win+r——>cmd右键管理员运行首先测试一下是否连接上:adbdevices输入adbshell,进入shell命令调试模式。adbshell回车之后输入pmlistpackage再回车就会出现这样的包名接下来,我们打开新的cmd,右键管理员身份运行输入adb

    2025年8月27日
    8
  • 发展,需求驱动 &#183; 一间 所见即所得

    发展,需求驱动 &#183; 一间 所见即所得

    2022年1月3日
    45

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号