07-网络篇-抓包分析TCP

为了抓包方便一些,我在ubuntu虚拟机运行服务端程序,而在windows运行客户端程序,关于客户端与服务端程序如下。
##1.程序
客户端:
vs_client.cpp

#include "stdafx.h"
#include 
#include 
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main(){
	WSADATA wsaData;
	if(WSAStartup(MAKEWORD(2,2),&wsaData)){
		cout<<"WinSock不能被初始化";
		WSACleanup();
		return 0;
	}
	SOCKET sockCli;
	sockCli=socket(AF_INET,SOCK_STREAM,0);
	SOCKADDR_IN addrSer;
	addrSer.sin_family=AF_INET;
	addrSer.sin_port=htons(8899);
	addrSer.sin_addr.S_un.S_addr=inet_addr("192.168.6.139");
	int res=connect(sockCli,(SOCKADDR *)&addrSer,sizeof(SOCKADDR));
	if(res){
		cout<<"客户端连接服务器失败"<<endl;
		return -1;
	}else{
		cout<<"客户端连接服务器成功"<<endl;
	}


	//3.向服务端发送消息
	char send_buf[256] = "hello server===>>>";
	char recv_buf[512];
	send(sockCli,send_buf,sizeof(send_buf),0);

	//4.接收服务端发来的消息
	int len = recv(sockCli,recv_buf,sizeof(recv_buf)-1,0);
	recv_buf[len] = '\0';
	printf("收到服务端的返回:%s\n",recv_buf);
	Sleep(100);


	closesocket(sockCli);
	WSACleanup();
	while(1)
	{
		Sleep(100);
	}
	return 0;
}

服务端:
sever.c

#include 
#include          
#include 
#include 
#include 
#include 

int main()
{
  //1.创建一个socket文件,也就是打开一个网络通讯端口
  int serv_sock = socket(AF_INET, SOCK_STREAM,0);
  
  //2.绑定服务器ip和端口到这个socket
  struct sockaddr_in serv_addr;
  memset(&serv_addr, 0, sizeof(serv_addr));//先清空一下初始的值,写上地址和端口号
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = inet_addr("192.168.6.139");//本机ip
  serv_addr.sin_port = htons(8899);//随意选了一个端口8899
  bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
  
  //3.将socket设置为监听状态
  listen(serv_sock,128);//设置最大连接数为128
  
  //4.准备接收客户端的请求连接,这里的步骤可以重复进行,接收多个客户端的请求
  while(1){
    //接收客户端的请求连接后,返回一个新的socket(clnt_sock)用于和对应的客户端进行通信
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);
    int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);

    //5.读取客户端发送来的数据
    char recv_buf[256];
    char send_buf[512]="hello client";
    int len = read(clnt_sock,recv_buf,sizeof(recv_buf)-1);
    recv_buf[len] = '\0';//字符串以“\0”结尾
    
    //6.打印出客户端发来的消息
    printf("recv client:%s\n",recv_buf);
    
    write(clnt_sock,send_buf,sizeof(send_buf));
	sleep(3);
    
    //8.关闭客户端连接
    close(clnt_sock);
	sleep(2);
    break;

  }
  //9.关闭服务端监听的socket
  close(serv_sock);
  return 0;
}

在Ubuntu上,我们可以通过ifconfig命令,查看到系统的IP地址。

$ ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.6.139  netmask 255.255.255.0  broadcast 192.168.6.255
        inet6 fe80::8bc1:6df8:a7f2:95c5  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:4a:43:e6  txqueuelen 1000  (Ethernet)
        RX packets 11886  bytes 3135825 (3.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13969  bytes 11893886 (11.8 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

##2.抓包
关于windows平台的抓包,这里不描述,本次测试抓的是服务端的包。
首先在线安装抓包软件:我们用是是tcpdump,此软件可以在网上找到开源代码,假如用于嵌入式环境抓包,大家可以自己在网上下载源码后交叉编译
sudo apt-get install tcpdump
运行下面命令持续抓包,每一个包抓60秒。 可以自己调节-G后面参数设置每一个包的抓包时长:
sudo tcpdump -i ens33 -s0 -G 60 -w %Y_%m%d_%H%M_%S.pcap

得到抓包图如下
抓包
##3.根据抓包分析Tcp
这里通过分析Ack和Seq号,让大家了解Tcp为什么是可靠传输

Seq(Sequence Number):
32bits,表示这个tcp包的序列号。tcp协议拼凑接收到的数据包时,根据seq来确定顺序,并且能够确定是否有数据包丢失。

Ack(Acknowledgment Number):
32bits,表示这个包的确认号。首先意味着已经收到对方了多少字节数据,其次告诉对方接下来的包的seq要从ack确定的数值继续接力
len:表示tcp携带的数据长度,不包括tcp头部信息的长度

下面根据上面抓包来说明:关于包的序号,我在抓包图中已标明
(1)三次握手
(客户端)1号包:我能和你建立连接
seq=0,表客户端第一条数据
没有ack,因为前面没有收到数据,所以不用确认已收到
Len=0。

(服务端)2号包:我收到了请求
seq=0,表此连接中,服务端第一条数据
ack=1 表示收到了客户端的seq=0的连接请求,告诉客户端接下来请从seq=1开始传输数据
Len=0,没有负载数据。

(客户端)3号包:连接建立。
seq=1,响应2号包
ack=1,收到服务器seq=0同意连接,告诉服务端从seq=1传输数据
Len=0

(2)数据传输
(客户端) 4号包:传256字节给服务端
seq=1,上次没有传输数据,seq号不变,也就是3号包的seq=1,len=0
ack=1,告诉服务端你要是发送数据,从seq=1开始
len=256,表示我这次传输的数据字节数

(服务端)5号包:响应。
seq=1,4号包的ack所要求的
ack=257,ack=(4号包的seq)+(4号包的len) = 1+256=257;下次客户端seq=257开始
len=0

(服务端)6号包:发512字节数据给客户端
5、6号均为服务端发送的包,在这期间没有接收到包,所以,5、6号包的seq、ack是一样的。
seq=1
ack=257
len=512,数据的长度

(客户端)7号包:响应
seq=257,5号包让从这个序列发
ack=513,ack=(6号包的seq)+(6号包的len)=1+512=513
len=0

(3)总结一下
3次握手的过程:
(1).起始包的seq都等于0
(2).三次握手中的ack=对方上一个的seq+1
(3).seq等于对方上次的ack号

数据发送过程:
(1).发送方的包包括seq和len,接收方通过ack=发送方的seq+发送方的len。
(2).三次握手时,客户端、服务端握手时,len=0,对方ack=seq+1。
而数据传输过程时,len=0,对方ack=seq+0

你可能感兴趣的:(网络篇,linux)