【计算机网络】Socket通信编程与传输协议分析

本文详细介绍了Socket通信的基本原理,包括TCP/TCP协议的三次握手和四次挥手过程,以及如何用Python实现Socket编程,包括端口扫描和TCP数据传输的分析。此外,还探讨了UDP通信特点和RST测试,以及遇到的问题及其解决方案。
摘要由CSDN通过智能技术生成

代码包及抓取文件传送门

Socket通信编程传输协议分析的python代码及wireshark抓取文件包

相关文章

【计算机网络】中小型校园网构建与配置

【计算机网络】IP协议及动态路由算法

【计算机网络】网络应用通信基本原理

目的:

1、掌握 Socket 通信协议实现的方法,能够使用至少一种语言编写通信程序;

2、理解 TCP 与 UDP 通信实现的方法和过程,理解报文段封装的作用,掌握可靠传输

实现的方法;

3、理解 TCP 三次握手建立连接、数据传输、四次挥手拆除连接的方法和过程。

要求:

  1. 使用Java/C/C++/C#/Python 等语言编写Socket 通信程序。
  2. 基于TCP 的 SOCKET 通信测试及验证。
  3. TCP 通信过程分析

原理:

1、Socket

Socket本质是编程接口(API),对 TCP/IP 的封装,TCP/IP 为网络服务和应用提供 Socket 编程接口,当前主要的 Socket 编程主要有SOCK_STREAM (TCP)、SOCK_DGRAM (UDP) 工作在传输层,SOCK_RAW 工作在网络层。

2、 TCP 报文封装及通信过程

TCP 在IP 层提供的不可靠服务基础上实现可靠数据传输服务,流水线机制传输,使用累积确认确认传输,并使用单一重传定时器和收到重复ACK 确认传输失败,进行重传。

TCP 段结构包含。

源地址端口、目的端口,16 位字段,发送接收该报文段的主机中应用程序的端口号。

序号(segment 第一个字节的编号)、确认号(接收方期望从对方接受的字节编号), Flag(URG: 紧急数据标志位;ACK:确认标志位;PSH:请求推送位,发送了数据;RST:连接复位;SYN:建立连接, 让连接双方同步序列号;FIN:释放连接)。

窗口大小:TCP 的窗口大小,以字节为单位。最大长度是 65535 字节(16 位)。

检验和:将传输层传输层伪首部与首部字段求和并校验,保证数据的完整性和准确性。

过程与结果

1端口扫描编程及本地服务器测试:
1.1 端口扫描结果

  这里我使用了python来编写端口扫描socket_connect的代码,部分代码如下:

from os import name
import threading
from socket import *
 
lock = threading.Lock()   # 确保 多个线程在共享资源的时候不会出现脏数据
openNum=0                       # 端口开放数量统计
threads=[]                       # 线程池
def portscanner(host,port):
    global openNum
    try:
        s=socket(AF_INET,SOCK_STREAM)
        s.connect((host,port))
        lock.acquire()
        openNum+=1
        print(f"{port} open")
        lock.release()
        s.close()
    except:
        pass
def main(ip,ports=range(65535)):        # 设置 端口缺省值0-65535
    setdefaulttimeout(1)
    for port in ports:
        t=threading.Thread(target=portscanner,args=(ip,port))
        threads.append(t)
        t.start()
    for t in threads:
        t.join()
    print(f"PortScan is Finish ,OpenNum is {openNum}")  
if __name__ == '__main__':
ip='127.0.0.1'
main(ip)                       # 全端口扫描

端口扫描结果

socket.connect_ex((ip, port)),端口开放则返回0,否则返回错误代码,扫描全端口,成功打开的端口显示在终端,并最后显示本次扫描的结果及端口数

1.2 wireshark抓包分析
1.2.1 TCP三次握手过程分析

TCP三次握手

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。

(1)第一次握手:客户端给服务端发一个 SYN 报文,并进入SYN_SEND状态,等待服务器确认。(SYN->1,其他为0)

此时首部的同步位SYN=1,初始序号seq=x(0),SYN=1的报文段不能携带数据,但要消耗掉一个序号。

(2)第二次握手:服务器收到syn包,必须确认客户的SYN,同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RECV状态。(ACK->1,SYN->1,其他为0),在确认报文段中SYN=1,ACK=1,确认号ack=x+1(1),初始序号seq=y(0)。 

(3)第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。(ACK->1,其他为0)

确认报文段ACK=1,确认号ack=y+1(1),序号seq=x+1(1)(初始为seq=x(0),第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。

1.2.2 TCP四次挥手过程分析

刚开始双方都处于ESTABLISHED状态,当客户端先发起关闭请求。

(1)第一次挥手:主动关闭方(客户端)发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了,但是,此时主动关闭方还可以接受数据。此时客户端处于FIN_WAIT1状态。(ACK->1,FIN->1)

即连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。

(2)第二次挥手:服务端收到FIN之后,会发送ACK报文,且把客户端的序列号值+1作为ACK报文的序列号值,表明已经收到客户端的报文了,此时服务端处于CLOSE_WAIT状态。(ACK->1)

即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。

(3)第三次挥手:被动关闭方(服务端)发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。此时服务端处于LAST_ACK的状态。(ACK->1,FIN->1)

即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。

(4)第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手,此时客户端处于 TIME_WAIT 状态。(ACK->1)

即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间后,客户端才进入CLOSED状态。

这里我出现了报错,报错截图如下:

主动关闭方正常收到FIN后,分析信息如下:

2、Socket通信编程代码:

我同样使用了python来进行socket通信的代码,以下是方法的解释:

sk=socket.socket() :括号里面包含两个参数,一个参数默认是ip地址蔟的socket.AF_INET,也就是IPv4;还有一个默认是传输TCP协议,也就是socket.SOCK_STREAM

sk.bind(()) :最里面的括号里包含两个参数,分别是客户端的ip地址与端口号

sk.listen(n): 里面的n表示阻塞连接n个进程,也就是最大等待数为n个,当连接上某个进程后,其他的都不会连接上,会处于等待状态,只有当断开与这个进程的连接,其他的进程才会依次连上

a,b=sk.accept():返回的是一个元组,元组的第一个参数是连接的信息,第二个参数是客户端的ip地址与端口号

a.recv(1024): recv()表示接受信息,里面的1024表示最多接受的字节数,如果有多余的字符就下次再进行传输

客户端(client)部分代码如下:

# client

BUFFSIZE = 1024
# connect
while True:
    iproot = ("10.225.146.226", 8888)#ip和端口 ip为服务端ipv4地址
    global socket_client
    socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        socket_client.connect(iproot)
    except Exception as message:
        print('connect error, %s' % message)
        time.sleep(1)
        continue
    else:
        print('connect sucess!')
        break

# main
while True:
    msg = input('client send: ')
    socket_client.send(msg.encode("utf-8"))
    a = socket_client.recv(1024)

    print('server send:' + a.decode("utf-8"))
    time.sleep(1)
    if msg == 'end':
        break

服务端(server)部分代码如下:

# server

iproot = ('', 8888)
BUFFSIZE = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(iproot)
s.listen(5)
print('waiting client..')

clientsocket, addr = s.accept()
while True:
  data = clientsocket.recv(BUFFSIZE)
  print(data.decode('utf-8')) 
  message=input("server send: ")
  clientsocket.send(message.encode('utf-8'))
  if message == 'end':
    break
3、进行socket通信测试,收到来自服务器端发来的消息:

执行代码,发送学号姓名的信息,客户端、服务端互相通信的结果即可,这里不做具体说明,以”end”退出通信

4、传输层协议及通信过程分析:

分析过程及序列号和确认号计算:

我以两台设备,互相通信,以下是对socket通信信息进行报文捕捉:

    分析过程及序列号和确认号计算:

分析:

通过TCP三次握手:

第一次握手seq=0。

第二次握手seq=0,ack=seq(第一次)+1=1。

第三次握手seq=seq(第一次)+1=1,ack=seq(第二次的)+1=1。

客户端发送 “20215120807lyh” 长度为14的字符串

此时[PSH,ACK] seq=1,ack=1,len=14

并且服务端返回ACK=1表示确认收到,此时seq=1,ack=1+14=15。

服务端发送 “20215120808ZSJ” 长度为14的字符串

此时[PSH,ACK] seq=1,ack=15,len=14

并且客户端返回ACK=1表示确认收到,此时seq=15,ack=15。

客户端向服务端发送断开请求以及服务端发送断开请求。(四次挥手)

第一次挥手seq=15,ack=15。

第二次挥手seq=ack(第一次)=15,ack=seq(第一次)+1=16。

第三次挥手seq=seq(第二次)=15,ack=ack(第二次)=16。

第四次挥手seq=ack(第三次)=16,ack=seq(第三次)+1=16。

5、Segment分段测试验证:
1.准备一个大于 1460 倍数的字符串:

准备的字符串长度:14+2*1460=2934(学号加姓名缩写占14,“xx”2*1460

2.进行 TCP 传输,在服务器端捕捉报文,分析分段情况:

发送一个长度为2934的字符串,分了3段,分别是1460,1460,14。

3.进行 UDP传输,在服务器端捕捉报文,分析分段情况:

没有三次握手UDP和网络层只提供尽力而为的服务,客户端未必能接收(无法分解到进程)

6、RST测试验证:

重连发生RST的抓包情况

发送数据时,因为服务端还未完全接收到缓冲区的数据而客户端断开连接可造

成RST包(RST->1,ACK->1)

操作异常问题与解决方案

问题1:与同学设备进行连接时失败,报错如下

解决方案:排查发现防火墙只关闭了域,使用公共局域网且没有关闭,关闭后解决

问题2:运行通信程序不断连接失败,并报错[WinError 10061] 由于目标计算机积极拒绝,无法连接

解决方案:由于服务端和客户端的ip地址和端口浑绕,改正后正常运行

  • 31
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值