计算机网络基于Wireshark的TCP/UDP协议分析(三次握手、四次挥手)与Socket应用设计(Visual Studio)

一、基于Wireshark的TCP/UDP协议分析(三次握手、四次挥手)

  1. 模拟抓包:分别在两台主机上通过cmd窗口运行server和client程序(见下文,目的是为了发送数据便于抓包分析,程序不重要),并进行文件传输。
    主机ip为192.168.43.141,端口为3025,传输文件名为textbook.txt,以传输数据块大小为10。
    举例运行截图如下:
    在这里插入图片描述
  2. 使用wireshark软件进行抓包分析,记录文件分割尺度分别为10、100、1460、6000字节时,分析记录网络传输数据块数、传输效率。
    在这里插入图片描述
    1、2、3前三条数据包表示三次握手的过程,12、16、17、18这四个数据包表示四次挥手的过程,可转化成下面的分析图解;从中可以看出序列号变化的规律,比如4号数据包Seq=1,数据长度Len=256;1+256=257,正好对应了5号数据包的Seq=257;4号数据包到11号数据包的序列号变化都符合这个规律,等于上一个序列号和数据长度之和。至于13到16号确认帧数据包是对前面客户端发送数据包(PSH=1)的应答出现在最后,是因为tcp通信是缓存流机制,客户端收到确认帧会存在延时,而不是客户端send()函数发送数据到缓存区马上会收到服务端的应答;考虑效率因素,等缓存区到达一定阈值后才可以传输,此时服务端才会发送应答,这就是确认帧ACK存在延时的原因。
  3. 实验结果以及分析

tcp建立连接:三次握手
在这里插入图片描述
可以看到客户端(左侧)通过TCP首部发送一个SYN包(seq=0,一个初始化的随机值)作为建立连接的请求等待确认应答。接着服务器(右侧)发送ACK包确认应答,发送SYN包请求连接,其中seq=0,ack=1(seq=0是服务端随机初始化的一个值,ack=1表示已经收到序列号为0的数据包,期待下一帧收到的序列号为1)。最后客户端(左侧)针对SYN包发送ACK包(seq=1, ack =1)确认应答,发送序列号为1的所需确认帧。

tcp释放连接:四次挥手
在这里插入图片描述
通常情况下释放TCP连接需要四个tcp端,每个方向上一个FIN和一个ACK。但是,第一个ACK和第二个FIN有可能被组合在同一个段中,从而减低到3个,比如12、17、18号数据包也是四次挥手的体现。结合抓包图可以看出,客户端捎带发出FIN,此时SEQ=10477+91=10568(存在最后一段数据长度Len=91);服务端回应ACK=10569,表示已经收到10568seq的请求,释放单方向连接。同理,服务端捎带发出FIN,此时SEQ=1,最后客户端相应ACK=2,表示已经收到SEQ=1的请求,从而释放另一方单向连接。至此,完成TCP连接释放。

  1. 记录网络上所传输的有效数据包的个数,填写下表,并进行分析。
    在这里插入图片描述
    通过修改每次不同的分割字节数,抓包分析得到经网络传输的数据块数,也就是带有Len长度不为0的数据块数。文件传输的过程中,带宽利用率是由于需要封装格外的帧头部字段,通过发送方总数据Len长度除以发送方发送帧的Length长度之和(如果小于60,需要以64计算,mac帧尾部为4个字节)则可以计算出带宽效率。

二、基于Visual Studio设计的socket文件传输代码以及运行截图

服务端代码以及分析注释如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#include <string.h>
#pragma comment(lib,"ws2_32.lib")
#include<iostream>
using namespace std;
struct FileName {//用来存储待传输文件名称的结构体
	char Fname[64];
	int len;
};
class FileReciever {//定义文件接收类,包含套接口、缓冲区、缓冲长度、写入文件指针、文件名等
private:
	int server;
	int client;
	sockaddr_in sa;
	sockaddr_in ca;
	char buff[1024];
	FILE *fp;
	FileName fn;
public:
	FileReciever()//构造函数初始化
	{
		server = socket(AF_INET, SOCK_STREAM, 0);//建立套接口
		client = socket(AF_INET, SOCK_STREAM, 0);
	}
	int Listen(int port)//封装端口监听函数
	{
		sa.sin_family = AF_INET;
		sa.sin_port = htons(port);
		sa.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
// 调用bind()将该套接口和本地网络地址联系在一起
		bind(server, (sockaddr*)&sa, sizeof(sa));
		listen(server, 5);//开始监听,最多容纳5个客户端连接请求,等待连接
		int len = sizeof(ca);
		printf("waiting for connect\n");
		client = accept(server, (sockaddr*)&ca, &len);
//调用accept函数从处于监听状态的流套接字server的客户连接请求队列中取出排在最前的客户请求,并且创建一个新的套接字来与客户套接字创建连接通道
		if (client == INVALID_SOCKET)
			cout << "failed" << endl;
		return 1;
	}
	int RecieveFile(const char *path,int block_size)//封装接受文件函数
	{
		char p[64];
		strcpy(p, path);
		int len = strlen(p);
		if (p[len - 1] != '\\')
		{
			p[len] = '\\';
			len++;
		}
		//用recv函数从TCP连接的另一端接收数据,client是接收端套接字,fn为存放接受数据的缓冲区
		recv(client, (char*)&fn, sizeof(fn), 0);
		strcat(p, fn.Fname);
		long long siz;
		recv(client, (char*)&siz, sizeof(siz), 0);
		siz = siz / block_size;
		fp = fopen(p, "wb+");
		long long int index = 0;
		int num;
		while (index <= siz)
		{
		    //block_size为每次接收的数据长度
			num = recv(client, buff, block_size, 0);
			if (num <= 0)
				break;
     		//通过fp文件指针将收到的缓冲区数据写入内存中
			fwrite(buff, (int)num, 1, fp);
			index++;
			//计算输出每次的传输进度
			cout << (int)index * 100 / siz << "%"<<endl;
			
		}
		printf("%s received successfully\n",p);//传输成功
		return 0;
	}
};
int main()
{
	WSADATA WSAData;
	WSAStartup(MAKEWORD(2, 2), &WSAData); //指定2.2版本的Socket,初始化通信接口
	FileReciever fr;
	fr.Listen(3025);
	char p[] = "E:\\test\\";
	int block_size = 1024;//默认传输长度
	fr.RecieveFile(p, block_size);
	system("pause");
}

客户端代码以及分析注释如下:

#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include<winsock2.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
struct FileName {//用来存储待传输文件名称的结构体
	char Fname[64];
	int len;
};
class FileSenderc{//定义文件发送类,包含套接口、缓冲区、缓冲长度、写入文件指针、文件名等
private:
	FILE * fp;
	SOCKET sock;
	sockaddr_in addr;
	FileName fn;
	char temp[1024];
public:
	FileSender()
	{
		sock = socket(AF_INET, SOCK_STREAM, 0);//建立套接口
	}
	int Connect(const char *ip, int port)//封装连接服务端函数
	{
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.S_un.S_addr = inet_addr(ip);
	 //客户程序调用connect函数来使客户与监听所指定的计算机的特定端口上的服务Socket进行连接
		if (connect(sock, (sockaddr*)&addr, sizeof(addr)) != 0)
		{
			cout << "failed.." << endl;
		}
		return 1;
	}
	int openFile(const char *path)//封装打开文件函数
	{
		char name[32], ext[16];
		_splitpath(path, NULL, NULL, name, ext);
		strcat_s(name, 32, ext);
		strcpy_s(fn.Fname, 32, name);
		fn.len = strlen(fn.Fname);
		fp = fopen(path, "rb");
		return 0;
	}
	int SendFile(const char *path,int port,int block_size) //封装发送文件函数
	{
		openFile(path);
		//客户程序用send函数向服务器发送请求
		send(sock, (char*)&fn, sizeof(FileName), 0);
		fseek(fp, 0, SEEK_END);
		long long siz = ftell(fp);
		fseek(fp, 0, SEEK_SET);
		send(sock, (CHAR*)&siz, sizeof(siz), 0);
		siz = siz / block_size;
		long long index = 0;
		int num;
		while (1)
		{	//每次读取长度为block_size的待发送内容
			num = fread(temp, 1, block_size, fp);
			if (num == 0)
				break;
			//发送读取的长度为num的文件内容
			send(sock, temp, num, 0);
			index++;
			//计算输出每次的传输进度
			cout << (int)index * 100 / siz << "%" << endl;
		}
		
		cout << "Successfully send at port " << port<< endl;
		return 0;
	}
};
int main()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);//初始化通信接口
	char str[100];
	cout << "Input file to be sent:" << endl;
	cin >> str;
	char path[] = "E:\\t4\\";//发送文件的路径
	strcat(path, str);
	FileSender fs;//设定待发送主机的ip地址和端口号
	if (fs.Connect("192.168.16.31", 3025))
		fs.SendFile(path,3025,1024);//默认传输长度为1024
	system("pause");
}

客户端运行截图及分析:
在这里插入图片描述
服务端运行截图及分析:
在这里插入图片描述

先在服务端主机启动服务端程序,listen()函数开始监听等待连接,接着在客户端输入目的主机(服务端)的ip地址以及端口号后,以及输入待传输的文件名称后;双方初始化通信套接字,等待双方建立连接。当客户端调用connect()连接成功后,利用c的fseek函数定位和fread函数读取待传输文件内容,调用send()函数发送给服务端,服务端通过recv()函数接受内容存至缓冲区,最后调用c`的fwrite()函数写入指定路径中。

三、实验总结

通过本次实验,我加深了对网络通信协议的底层机制认识。首先,我基于visual studio编写Winsock接口的服务端和客户端程序实验文件传输。在这个过程中,我认识到传输过程中存在一个缓冲区,数据达到一定阈值后才进行传递,理解tcp机制。接着,通过wireshark抓包软件进行抓包分析tcp三次握手、四次挥手以及总结序列号变化的规律,真正地将理论所学知识进行验证,分析seq、ack等变化规律。起初在计算带宽效率的时候没有考虑到以太网有效帧不足64字节长需要填充够64位导致计算有误,后面进行修正完成实验。

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值