TCP协议:传输协议,基于端口工作

  三次握手,四次挥手

  TCP协议建立双向通道。

三次握手, 建连接:

1:客户端向服务端发送建立连接的请求
2:服务端返回收到请求的信息给客户端,并且发送往客户端建立连接的请求
3:客户端接收到服务端发来的请求,返回接成功给服务端,完成双向连接

第一客戶向服务端发送请求,请求建立连接
服务端同客户端的请求,并同时向客户端发送建立
连接的请求,最后客户端同意后建立 双向连接。
C ----> S
C <---- S

- 反馈机制:
  客户端往服务端发送请求,服务端必须返回响应,
  告诉客户端收到请求了,并且将服务端的数据一并返回给客户端。
  C-->S: 一次请求,必须有一次响应。

  - 缺点:
    - 洪水攻击:
      指的是通过伪造大量的请求,往对方服务器发送请求,
      导致对方服务器响应跟不上,以至于瘫痪。
      linux系统有个参数可以限制。

    - 半连接池listen: 限制用户在同一个时间段内的访问数量。

四次挥手, 断开连接:

1:客户端向服务端发送断开连接的请求
2:服务端返回收到请求的信息给客户端
3:服务端确认所有数据接收完成以后,再发送同意断开连接的请求给客户端
4:客户端返回收到断开连接的请求,给服务端。

socket套接字通信:
- 什么是socket?
  socket是一个模块, 又称套接字,用来封装 互联网协议(应用层以下的层)。

- 为什么要有socket?
  socket可以实现 互联网协议应用层以下的层的工作。
  - 提高开发效率

- 怎么使用socket?
  import socket
  写socket套接字:
    Client
    Server

socket初级版

注意:server和client的发送接收命令必须一一对应,recv接收命令必须收到内容能会运行

windows系统下,即使发送消息为空,也有数据头,接收消息不为空;而linux下,接收消息则会为空

'''
先启动套接字服务端 server代码
'''
import socket # 买手机
server = socket.socket() # 绑定手机卡
server.bind(
# 相当于手机号码
# 127.0.0.1 == localhost 本机回环地址
# 单机测试下: 127.0.0.1
('127.0.0.1', 9527)
) # 半连接池
server.listen(5) # 最多5个人坐椅子, 实际上==6 print(
'server is running...'
) # 等待电话接入
# conn: 指的是服务端往客户端的管道
conn, addr = server.accept() # 接听对方讲话的内容
# data客户端发送过来的消息
data = conn.recv(1024) # 可接受一次1024bytes的数据
print(data) # 挂电话
conn.close()
'''
启动服务端后再启动客户端 Client代码
'''
import socket # 买手机
client = socket.socket() # 拨号
client.connect(
# 客户端的ip与port必须与服务端一致
('127.0.0.1', 9527)
) print(
'client is running...'
) # 必须发送bytes类型的数据
# client.send('hello'.encode('utf-8'))
# 讲话给对方听
client.send(b'hello')

2)socket升级版

'''
注意:
客户端先一次发送,服务端得先一次接受,再发送消息。
'''
import socket # 买手机
server = socket.socket() # 绑定手机卡
server.bind(
# 相当于手机号码
# 127.0.0.1 == localhost 本机回环地址
# 单机测试下: 127.0.0.1
('127.0.0.1', 9527)
) # 半连接池
server.listen(5) # 最多5个人坐椅子, 实际上==6 print('server is running...') # 等待电话接入
# conn: 指的是服务端往客户端的管道
conn, addr = server.accept() # 接听对方讲话的内容
# data客户端发送过来的消息
data = conn.recv(1024) # 可接受一次1024bytes的数据
print(data) # 服务端往客户端发送消息
conn.send(b'hi im server') # 挂电话
conn.close()
'''
启动服务端后再启动客户端
'''
import socket # 买手机
client = socket.socket() # 拨号
client.connect(
# 客户端的ip与port必须与服务端一致
('127.0.0.1', 9527)
) print('client is running...') # 必须发送bytes类型的数据
# client.send('hello'.encode('utf-8'))
# 讲话给对方听
client.send(b'hello im client...')
data = client.recv(1024)
print(data) client.close()

3)socket升级版

'''
注意:
客户端先一次发送,服务端得先一次接受,再发送消息。 server
'''
import socket
# 买手机
server = socket.socket() # 绑定手机卡
server.bind(
# 相当于手机号码
# 127.0.0.1 == localhost 本机回环地址
# 单机测试下: 127.0.0.1
('127.0.0.1', 9527)
)
# 半连接池
server.listen(5) # 最多5个人坐椅子, 实际上==6
print('server is running...')
# 等待电话接入
# conn: 指的是服务端往客户端的管道
conn, addr = server.accept()
while True:
# 接听对方讲话的内容
# data客户端发送过来的消息
data = conn.recv(1024) # 可接受一次1024bytes的数据
if len(data) == 0: # 没有意义,windows系统下不可能为0
break
if data.decode('utf-8') == 'q':
break
print(data)
send_data = input('服务端>>>:')
# 服务端往客户端发送消息
conn.send(send_data.encode('utf-8'))
# 挂电话
conn.close()
'''
启动服务端后再启动客户端 client
'''
import socket # 买手机
client = socket.socket() # 拨号
client.connect(
# 客户端的ip与port必须与服务端一致
('127.0.0.1', 9527)
) print('client is running...') # 必须发送bytes类型的数据
# 讲话给对方听
while True:
send_data = input('客户端>>>:')
client.send(send_data.encode('utf-8'))
data = client.recv(1024)
if data.decode('utf-8') == 'q':
break if len(data) == 0:
break print(data) client.close()

4)socket最终版

'''
注意:
客户端先一次发送,服务端得先一次接受,再发送消息。 server
'''
import socket
# 买手机
server = socket.socket() # 绑定手机卡
server.bind(
# 相当于手机号码
# 127.0.0.1 == localhost 本机回环地址
# 单机测试下: 127.0.0.1
('127.0.0.1', 9527) # 局域网内测试
# ('192.168.12.202', 9527) )
# 半连接池
server.listen(5) # 最多5个人坐椅子, 实际上==6
print('server is running...') # 循环实现可接受多个用户访问
while True:
# 等待电话接入 ---> 接入客户端
# conn: 指的是服务端往客户端的管道
conn, addr = server.accept()
print(addr) # 循环实现循环通信
while True:
try: # 监听代码块是否有异常出现
# 接听对方讲话的内容
# data客户端发送过来的消息
data = conn.recv(1024) # 可接受一次1024bytes的数据 if len(data) == 0:
break
if data.decode('utf-8') == 'q':
break
print(data.decode('utf-8'))
send_data = input('服务端>>>:')
# 服务端往客户端发送消息
conn.send(send_data.encode('utf-8')) # 捕获异常信息,并打印 # e: 报错信息
except Exception as e:
print(e)
break # 挂电话
conn.close()
'''
启动服务端后再启动客户端 client
'''
import socket # 买手机
client = socket.socket() # 拨号
client.connect(
# 客户端的ip与port必须与服务端一致
('127.0.0.1', 9527)
) print('client is running...') # 必须发送bytes类型的数据
# 讲话给对方听
while True:
send_data = input('客户端>>>:')
client.send(send_data.encode('utf-8'))
data = client.recv(1024) if data.decode('utf-8') == 'q':
break if len(data) == 0:
break print(data.decode('utf-8')) client.close()

3.粘包问题
- 1) 问题: 无法确认对方发送过来数据的大小。

# server
import socket
import subprocess server = socket.socket() server.bind(
('127.0.0.1', 9000)
) server.listen(5) while True:
conn, addr = server.accept()
print(addr) while True:
try:
# recv从内存中获取数据
cmd = conn.recv(10) if len(cmd) == 0:
continue
cmd = cmd.decode('utf-8') # dir
if cmd == 'q':
break # 调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
obj = subprocess.Popen(
# cmd接收解码后的字符串
cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
) # 结果交给result变量名
result = obj.stdout.read() + obj.stderr.read()
print(len(result))
# gbk
print(result.decode('gbk')) # 将结果返回给客户端
conn.send(result) except Exception as e:
print(e)
break conn.close()
#client
import socket client = socket.socket() client.connect(
('127.0.0.1', 9000)
) while True: cmd = input('客户端输入的内容: ') client.send(cmd.encode('utf-8')) data = client.recv(19190) # 无法估计大小
print(len(data))
print(data.decode('gbk'))

- 2) 问题: 在发送数据间隔短并且数据量小的情况下,
会将所有数据一次性发送。

# server
import socket
server = socket.socket() server.bind(
('127.0.0.1', 9000)
) server.listen(5) conn, addr = server.accept() data = conn.recv(1024)
print(data) # b'hellohellohello' data = conn.recv(1024)
print(data) # b'' data = conn.recv(1024)
print(data) # b''
# client
import socket client = socket.socket() client.connect(
('127.0.0.1', 9000)
) client.send(b'hello')
client.send(b'hello')
client.send(b'hello')

解决: 确认对方数据的大小。

4.解决粘包问题(struct模块)
- 无论哪一端先发送数据
- 客户端
- 1) 先制作报头,并发送 (struct)
- 2) 发送真实数据

- 服务端:
- 1) 接收报头,并解包获取 真实数据长度
- 2) 根据真实数据长度 接收真实数据
recv(真实数据长度)

'''
1.struct是什么?
是一个python内置的模块, 它可以将固定长度的数据,打包成固定格式的长度。 # 模式
i: 4 # 其他模式 2.作用:
可以将真实数据,做成一个固定长度多的报头,客户端发送给服务端,服务端可以接收报头,
然后对报头进行解包,获取真实数据的长度,进行接收即可。
''' import struct data = b''
print(len(data))
# 打包制作报头
header = struct.pack('i', len(data))
print(header)
print(len(header)) # 解包获取真实数据长度 ---> 得到一个元组,元组中第一个值是真实数据的长度
res = struct.unpack('i', header)[0]
print(res)

案例一:

# server
import socket
import subprocess
import struct server = socket.socket() server.bind(
('127.0.0.1', 9000)
) server.listen(5) while True:
conn, addr = server.accept()
print(addr) while True:
try:
# 获取客户端传过来的报头
header = conn.recv(4)
# 解包获取真实数据长度
data_len = struct.unpack('i', header)[0] # 准备接收真实数据
cmd = conn.recv(data_len) if len(cmd) == 0:
continue cmd = cmd.decode('utf-8') # dir
if cmd == 'q':
break # 调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
obj = subprocess.Popen(
# cmd接收解码后的字符串
cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
) # 结果交给result变量名
result = obj.stdout.read() + obj.stderr.read()  # 二进制,不用转码 print('发给客户端端的真实数据长度', len(result)) # gbk
# print(result.decode('gbk')) # 将结果返回给客户端
# 做一个报头, 返回给客户端
header = struct.pack('i', len(result))
print(len(header))
conn.send(header)
conn.send(result)     except Exception as e:
print(e)
break conn.close()
#client
import socket
import struct client = socket.socket() client.connect(
('127.0.0.1', 9000)
) while True: cmd = input('客户端输入的内容: ') cmd_bytes = cmd.encode('utf-8') # 做一个报头
header = struct.pack('i', len(cmd_bytes))
print(len(header))
client.send(header) # 待服务端确认长度后,发送真实数据长度
client.send(cmd_bytes) # 接收服务端返回的报头
headers = client.recv(4) # 解包,接收服务端返回的真实数据
data_len = struct.unpack('i', headers)[0]
result = client.recv(data_len) print('接收服务端返回的真实数据长度', len(result))
print(result.decode('gbk'))

案例二:

#server
import socket
import json
import struct server = socket.socket() server.bind(
('127.0.0.1', 9000)
) server.listen(5) while True:
conn, addr = server.accept()
print(addr) while True:
try:
# 获取客户端传过来的报头
header = conn.recv(4) # 解包获取真实数据长度
json_len = struct.unpack('i', header)[0] # 接收json(dict)的真实长度
json_bytes_data = conn.recv(json_len) # 将bytes类型数据转为json数据
json_data = json_bytes_data.decode('utf-8') # 反序列化 json ---> dict
back_dic = json.loads(json_data)
print(back_dic)
print(back_dic.get('movie_len')) # 准备接收真实数据
# movie_data = conn.recv(back_dic.get('movie_len'))
# print(movie_data) except Exception as e:
print(e)
break conn.close()
#client
import socket
import struct
import json client = socket.socket() client.connect(
('127.0.0.1', 9000)
) while True: movie_name = input('请输入上传电影的名字: ') # 伪装电影真实数据
movie_len = 1000000 send_dic = {
'movie_name': movie_name,
'movie_len': movie_len
} # 序列化
json = json.dumps(send_dic)
print(json)
print(json.encode('utf-8'))
print(len(json.encode('utf-8'))) json_bytes = json.encode('utf-8') # 做一个报头
header = struct.pack('i', len(json_bytes)) # 先发送报头
client.send(header) # 后发送真实数据
client.send(json_bytes)

网络编程 TCP协议:三次握手,四次回收,反馈机制 socket套接字通信 粘包问题与解决方法的更多相关文章

  1. 网络编程——TCP协议、UDP协议、socket套接字、粘包问题以及解决方法

    网络编程--TCP协议.UDP协议.socket套接字.粘包问题以及解决方法 TCP协议(流式协议) ​ 当应用程序想通过TCP协议实现远程通信时,彼此之间必须先建立双向通信通道,基于该双向通道实现数 ...

  2. 网络架构,七层协议,三次握手四次挥手,socket套接字简单编程

    一.单机架构 应用领域: 植物大战僵尸 office 二.CS架构 应用领域: QQ 大型网络游戏 计算机发展初期用户去取数据,直接就去主机拿,从这里开始就分出了客户端和服务端. 客户端:用户安装的软 ...

  3. TCP协议 三次握手四次挥手

    当某个应用端想基于TCP协议与另一个应用端通信时,它会发送一个通信请求. 这个请求必须被送到一个确切的地址.在双方“握手”之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex) ...

  4. TCP协议—三次握手四次挥手的原理<转>

    三次握手四次挥手的原理   TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接.在TCP/IP协议中,TCP 协议提供可靠的连接服务,连接是通过三次握手进行初始化的.三 ...

  5. Tcp协议三次握手四次挥手

    一.什么是TCP TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的.可靠的. 基于IP的传输层协议.TCP在IP报文的协议号是6. 二.什 ...

  6. TCP Socket 套接字 和 粘包问题

    一.Scoket 套接字 Scoket是应用层(应用程序)与TCP/IP协议通信的中间软件抽象层,它是一组接口.也可以理解为总共就三层:应用层,scoket抽象层,复杂的TCP/IP协议 基于TCP协 ...

  7. Python进阶----网络通信基础 ,OSI七层协议() ,UDP和TCP的区别 , TCP/IP协议(三次握手,四次挥手)

    Python进阶----网络通信基础 ,OSI七层协议() ,UDP和TCP的区别 , TCP/IP协议(三次握手,四次挥手) 一丶CS/BS 架构 C/S: 客户端/服务器    定义:       ...

  8. 在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP

    如果对网络工程基础不牢,建议通读<细说OSI七层协议模型及OSI参考模型中的数据封装过程?> 下面就是TCP/IP(Transmission Control Protoco/Interne ...

  9. [na]TCP的三次握手四次挥手/SYN泛洪

    1.TCP报文格式 上图中有几个字段需要重点介绍下: (1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记. (2)确认序号:Ack序号,占32位, ...

随机推荐

  1. nginx小知识

    What Nginx是一款轻量级的Web服务器.反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用. 反向代理 当我们在外网访问百度的时候,其实会进行一个转发,代理到内 ...

  2. 黑科技!两行代码完美解决:同时设置overflow-x:hidden,overflow-y:visible无效的问题

    不废话,直接上代码 <!DOCTYPE html> <html> <head> <style> body { padding: 0; margin: 0 ...

  3. Scala中sortBy和Spark中sortBy区别

    Scala中sortBy是以方法的形式存在的,并且是作用在Array或List集合排序上,并且这个sortBy默认只能升序,除非实现隐式转换或调用reverse方法才能实现降序,Spark中sortB ...

  4. C# 分解文件路径目录

    利用正则表达式分解文件目录 [^\\].*?[\\$]|[^\\].*?\.\w+|\w+ 测试字符串:C:\Users\wppcn\Desktop\中文长字符第一次测试\新建文件夹1\新建文件夹2\ ...

  5. Python课程第八天作业

    第一题: 1.自定义一个 Fruit 类:该类有一个 类属性: identify:值为"水果",有两个对象属性: name,price:值由实例化对象时赋值,一个类方法: get_ ...

  6. react的标记渲染机制

    // ReactUpdates.js  - enqueueUpdate(component) function dirtyComponents.push(component); https://jue ...

  7. Visual Studio 2019 正式版今日发布 key

     Visual Studio 2019 EnterpriseBF8Y8-GN2QH-T84XB-QVY3B-RC4DFVisual Studio 2019 ProfessionalNYWVH-HT4X ...

  8. python 练习题:计算的BMI指数,并根据BMI指数条件选择

    小明身高1.75,体重80.5kg.请根据BMI公式(体重除以身高的平方)帮小明计算他的BMI指数,并根据BMI指数:低于18.5:过轻18.5-25:正常25-28:过重28-32:肥胖高于32:严 ...

  9. MySQL——python交互

    与python交互之前我们需要安装一个MySQL的驱动模块Connector,这个驱动模块直接在cmd命令行输入 pip install mysql.connector 安装是否成功可以接着输入 py ...

  10. js数组去重 数组拼接 替换数组中的指定值 递归数组 判断数组中是否存在指定值 数组求和 根据条件判数组值

    这是学习过程中记录的一些关于数组操作的常用属性或方法,记录一下方便以后使用. // 数组去重 var arr1 = [1,1,2,3,4,5,6,3,2,4,5,'a','b','c','a',6,7 ...