TCP抓包

前言

基于VS2015写一个socket通信的Client/Server小程序,验证TCP协议的三次握手与四次挥手。

Server:监听本机的8888端口。

Client:向本机地址(127.0.0.1:8888)发送信息。

工具

Wireshark

代码

Server代码:

#include <WinSock2.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib") 

const int port = 8888;

int main()
{
	//初始化WSA(windows下需要用WSAStarup)
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0)
	{
		return 0;
	}

	// 创建套接字
	SOCKET sock;

	sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock == INVALID_SOCKET)
	{
		cout << "Socket error!" << endl;
		return -1;
	}
	
	// 将地址绑定到port 和 ip
	struct sockaddr_in name;
	name.sin_family = AF_INET;
	name.sin_port = htons(8888);
	name.sin_addr.S_un.S_addr = INADDR_ANY;	// 通配地址

	// 绑定套接字
	if (bind(sock, (LPSOCKADDR)&name, sizeof(name)) == SOCKET_ERROR)
	{
		cout << "bind error!" << endl;
		return -1;
	}

	// 监听
	if (listen(sock, 5) == SOCKET_ERROR)
	{
		cout << "listen error!" << endl;
	}

	// 等待连接
	char data[102400] = { 0 };
	SOCKET sClient;
	sockaddr_in remoteAddr;
	int nAddrlen = sizeof(remoteAddr);	// 要初始化,否则默认为0,accept()出错
	while (1)
	{
		cout << "等待连接..." << endl;
		// 接受一个客户端的连接请求
		sClient = accept(sock, (SOCKADDR*)&remoteAddr, &nAddrlen);
		if (sClient == INVALID_SOCKET)
		{
			cout << "accept error!" << endl;
			continue;
		}
		cout << "接受到一个连接: " << inet_ntoa(remoteAddr.sin_addr) << endl;

		// 接受数据
		int ret = 0;
		int realSize = 0;
		int length = sizeof(data);
		while (1)
		{
			ret = recv(sClient, (char*)(data + ret), length, 0);
			if (ret <= 0)
			{
				break;
			}
			length -= ret;
			realSize += ret;
		}
		data[realSize - 1] = '\0';
		cout << "接收:" << realSize << endl;
		closesocket(sClient);
	}
	return sock;
}

 

Client代码:

#include <WinSock2.h>
#include <iostream>
using  namespace std;
#pragma comment(lib, "ws2_32.lib")

#define SERVER_ADDRESS "127.0.0.1" //服务器端IP地址 
#define PORT           8888         //服务器的端口号   
const int MESSIGE_SIZE = 102400;

void Send_data(int sockfd)
{
	char* query;
	query = new char[MESSIGE_SIZE + 1];
	for (int i = 0; i < MESSIGE_SIZE; i++)
	{
		query[i] = 'a';
	}
	query[MESSIGE_SIZE] = '\0';

	size_t remaining = strlen(query);
	while (remaining)
	{
		int n_written = send(sockfd, query, remaining, 0);
		cout << "send into buffer " << n_written << endl;
		if (n_written <= 0)
		{
			cout << "send failed" << endl;
			return;
		}

		remaining -= n_written;
		query += n_written;
	}

}

int main()
{
	//初始化WSA(windows下需要用WSAStarup)
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0)
	{
		return 0;
	}

	// 创建套接字和服务器地址
	int sockfd;
	sockaddr_in server_addr;
	
	// 初始化套接字和服务器地址
	sockfd = socket(PF_INET, SOCK_STREAM, 0);

	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(8888);
	server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_ADDRESS);

	// 建立连接
	if ( connect(sockfd, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR )
	{
		cout << "连接失败" << endl;
		closesocket(sockfd);
	}

	// 发送数据
	Send_data(sockfd);

	closesocket(sockfd);
	return 0;
}

抓包

工具使用方法:

  • 接口:Adapter for loopback traffic capture(适于本机)
  • 过滤器:tcp.port == 8888 and (ip.src == 127.0.0.1 or ip.dst == 127.0.0.1)

抓包结果:

分析

三次握手(详细可参考:https://blog.csdn.net/qq_34386891/article/details/80515912

三次握手
发送接收标志位同步信息补充
ClientServerSYN=1seq=0, len=0发送自己的序号
ServerClientACK=1, SYN=1ack=1, seq=0, len=0回复的ack=Client的seq + 1,并发送自己的序号
ClientServerACK=1ack=1, seq=1, len=0发送自己的序号,自己的序号就是对面回复的ack

发送数据

本例Client向Server发送120400个字节数据,client选的是阻塞式发送,向缓冲区发送102400字节,缓冲区装不下了会阻塞在send函数,Server读取了内容,缓冲区空出空间就可以继续向缓冲区写数据。本例的102400字节,向缓冲区写了2次才写完。

发送数据
发送接收标志位同步信息补充
ClientServerACK=1ack=1, seq=1, len=65495回复对方的下一帧序号,发送自己的序号、数据长度
ServerClientACK=1ack=65496, seq=1, len=0回复的ack=Client的seq + Client的len,并发送自己的序号、数据长度
ClientServerPSH=1,ACK=1ack=1, seq=65496, len=36905

PSH=1,提醒Server尽快取走报文;

回复的ack=Server的seq + Server的len,并发送自己的序号、数据长度

ServerClientACK=1ack=102401, seq=1, len=0回复的ack=Client的seq + Client的len,并发送自己的序号、数据长度

 

四次挥手 

四次挥手
发送接收标志位同步信息 
ClientServerFIN=1,ACK=1ack=1, seq=102401, len=0回复对方的下一帧序号,发送自己的序号
ServerClientACK=1ack=102402, seq=1, len=0回复的ack=Client的seq + 1,并发送自己的序号
ServerClientFIN=1,ACK=1ack=102402, seq=1, len=0回复对方下一帧的序号,发送自己的序号
ClientServerACK=1ack=2, seq=102402, len=0回复的ack=Server的seq + 1,并发送自己的序号

 

总结

  • 在握手和挥手阶段,ack=seq+1;
  • 在发送数据阶段,ack=seq+len;
  • 当发送缓冲区占满,下一帧数据会置PSH=1,提醒接收端尽快取走数据。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值