网络2-tcp 握手 挥手- socket 套接字 -黏包·

39章节


传输层

应用层

socket套接字

代码优化

半连接池

黏包问题


传输层

1.PORT 协议 
2.TCP 与UDP协议
	规定了数据传输所遵循的规则
ps:数据传输能够遵循的协议有很多,TCP和UDP是较为常见的两个
  • TCP(传输控制协议)

    什么是tcp协议?

    传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议
    

    特点

     面向链接的运输协议;
     每个tcp链接只能有两个端点;
     tcp提供可靠的交付的任务
     TCP提供全双工通信
     面向字节流
     容易遭受洪水攻击,就是让大量客户端朝服务器请求
    
    • *TCP传输

    基于TCP传输数据非常的安全 因为有双向通道
    	基于TCP传输数据,数据不容易丢失!!! 不容易丢失的原因在于二次确认机制
    		每次发送数据都需要返回确认消息 否则在一定的时间会反复发送
    
    • TCP三次握手
      建议双向通道
      

      查看源图像

    • TCP四次挥手
      断开双向通道,中间的两部不能合并
      

image

  • udp协议

  • 是一种面向事务的简单不可靠信息传输服务

    • 特点

      基于UDP协议发送数据 没有任何的通道也没有任何的限制
      UDP发送数据没有TCP安全(没有二次确认机制)
      

应用层

主要取决于程序员自己采用什么策略和协议
常见协议有:HTTP ,https,ftp

socket套接字

基于文件类型的套接字家族
	套接字家族的名字:AF_UNIX
基于网络类型的套接字家族
	套接字家族的名字:AF_INET
  • 套接字代码

  • 运行程序的时候 肯定是先确保服务端运行 之后才是客户端

import socket
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.开业 等待接客
sock, address = server.accept()
print(sock, address)  # sock是双向通道 address是客户端地址
# 5.数据交互
sock.send(b'hello big baby~')  # 朝客户端发送数据
data = sock.recv(1024)  # 接收客户端发送的数据 1024bytes
print(data)
# 6.断开连接
sock.close()  # 断链接
server.close()  # 关机


import socket
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
data = client.recv(1024)  # 接收服务端发送的数据
print(data)
client.send(b'hello sweet server')  # 朝服务端发送数据
# 4.关闭
client.close()
  • 套接字代码优化

    1.send与recv
    ​ 客户端与服务端不能同时执行同一个
    ​ 有一个收 另外一个就是发
    ​ 有一个发 另外一个就是收
    ​ 不能同时收或者发!!!


    2.消息自定义

    input获取用户数据即可(主要编码解码)-


    3.循环通信

    ​ 给数据交互环节添加循环即可


    4.服务端能够持续提供服务

    ​ 不会因为客户端链接报错

    ​ 异常捕获,一旦客户端链接,服务端结束通信循环,调到连接等待


    5.消息不能为空

    ​ 判断是否为空,如果是则重新输入(主要针对客户端)


    6.服务端频繁重启可能端口被占的错(主要针对mac电脑)

    ​ from socket import SOL_SOCKET,SO_REUSEADDR
    server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加


    7.客户端异常退出会发送空消息(针对mac linux)
    ​ 针对接收的消息加判断处理即可


半连接池

  server.listen(5)
  	主要是为了做缓冲 避免太多无效等待

黏包问题

  • 黏包原因

1.TCP特性
​ 流式协议:所有的数据类似于水流 连接在一起的
​ ps:数据量很小 并且时间间隔很多 那么就会自动组织到一起
2.recv
​ 我们不知道即将要接收的数据量多大 如果知道的话不会产生也不会产生黏包

  • 黏包问题复现代码

服务端代码
	 sock.recv(1024)
    sock.recv(1024)
    sock.recv(1024)

客户端代码
	 client.send(b'jason')
	 client.send(b'kevin')
    client.send(b'tony')
  • 黏包解决方式

  • struct

    #struct模块无论数据长度是多少 都可以帮你打包成固定长度
    #然后基于该固定长度 还可以反向解析出真实长度
    import struct   #调用struct
    

    解决复现案例

    import struct
    
    # info = '下午上课 以后可能是常态!'
    # print(len(info))  # 13  数据原本的长度
    # res = struct.pack('i', len(info))  # 将数据原本的长度打包
    # print(len(res))  # 4  打包之后的长度是4
    # ret = struct.unpack('i', res)  # 将打包之后固定长度为4的数据拆包
    # print(ret[0])  # 13  又得到了原本数据的长度
    
    # info1 = '打起精神啊 下午也需要奋斗 也需要认真听 客服困难 你困我也困!!!'
    # print(len(info1))  # 34
    # res = struct.pack('i', len(info1))  # 将数据原本的长度打包
    # print(len(res))  # 4  打包之后的长度是4
    # ret = struct.unpack('i', res)
    # print(ret[0])  # 34
    
    • struct模块特点

    ​ 无论数据长度是多少 都可以帮你打包成固定长度
    ​ 然后基于该固定长度 还可以反向解析出真实长度

    思路 客户端和服务端
        1.先将真实数据的长度制作成固定长度 4
        2.先发送固定长度的报头
        3.再发送真实数据
    
        1.先接收固定长度的报头  4
        2.再根据报头解压出真实长度 
        3.根据真实长度接收即可
    
    • struct模块注意

      	struct模块针对数据量特别大的数字没有办法打包!!!
      
posted @ 2022-08-07 22:18  名字长的像一只老山羊  阅读(26)  评论(0编辑  收藏  举报