网络编程(二)

网络编程(二)


昨日回顾


  1. 软件开发架构

    C/S客户端架构与B/S浏览器架构

  2. 网络编程

    实现计算机数据的远程交互,想实现远程交互需要有物理介质

  3. 互联网协议

    即OSI七层协议:应用层、表示层、会话层、传输层、网络层、数据链路层、物理链接层

    也被称为五层协议:应用层、传输层、网络层、数据链路层、物理链接层

  4. 互联网

    将计算机相互链接到一起,实现计算机之间的数据交互

  5. 网络相关设施

    交换机、局域网、广播于单播、路由器等

  6. 协议

    TCP协议:回话与传输需要对方同意的协议,三次握手四次挥手

    UDP协议:不需要对方同意,无通道,直发起信息传输


今日学习内容


socket


socket套接字简介


如果每次编写C/S架构的程序,都用代码去按照OSI七层架构去完成,那么过程是相当的复杂,所以有一个模块可以帮忙代劳,这个模块就是利用了socket套接字技术这门技术

ps:socket是最底层的原理,很多框架都封装了,不需要过多研究。


socket模块


首先编写一个C/S架构的软件,我们肯定是优先考虑其服务端,正常逻辑问题。


服务端

import socket

server = socket.socket()  # 第一步,创建基础,准备好联入网络的协议
‘括号不写入参数,将会使用默认形参,使用TCP协议的套接字’

server.bind(('127.0.0.1', 8080))  # 第二步,选择地址与端口号
‘127.0.0.1是计算机的本地回环地址,只有本机可以访问到,8080是端口号,一般是填入8000后就行’

server.listen(5)  # 第三步,成功链接入网络
‘参数5代表了这个半连接池可以最大有五个用户等待链接’

sock, addr = server.accept()  # 等待用户来发起会话,若无用户便会造成程序阻塞
‘sock是接受的信息,addr是客户端地址’

data = sock.recv(1024)  # 服务端接受客户端信息

sock.send('你好啊'.encode('utf8'))  # 服务端回复客户端的信息

'''recv和send接收和发送的都是bytes类型的数据'''

sock.close()  # 终止与当前客户端的会话
server.close()  # 终止所有链接,关闭服务端

客户端

import socket


client = socket.socket()  # 产生一个socket对象

client.connect(('127.0.0.1', 8080))  # 根据服务端的地址和端口号链接

client.send(b'hello sweet heart!!!')  # 给服务端发送消息

data = client.recv(1024)  # 接收服务端回复的消息

print(data.decode('utf8'))  # 解码后打印出服务端的信息

client.close()  # 关闭客户端

服务端与客户端首次交互
一边是recv那么另一边必须是send 两边不能相同 否则就'冷战'了

美苏冷战


通信循环


前言:要实现通信循环并不难,但是我们需要使用各种判断代码,来确使代码不会出现过多的异常而导致通信循环的中断。

1.先解决消息固定的问题
	利用input获取用户输入
2.再解决通信循环的问题
	将双方用于数据交互的代码循环起来

服务端
while True:
    data = sock.recv(1024)  # 接受客户端的消息
    print(data.decode('utf8'))
    msg = input('请回复消息>>>:').strip()
    sock.send(msg.encode('utf8'))  # 给客户端发送消息
 
客户端
while True:
    msg = input('请输入你需要发送的消息>>>:').strip() 
    client.send(msg.encode('utf8'))  # 给服务端发送消息
    data = client.recv(1024)  # 接受服务端回复的消息
    print(data.decode('utf8'))

优化通信循环链接代码


1.发送消息不能为空
	统计长度并判断即可
  
2.反复重启服务端可能会报错>>>:address in use
  这个错在苹果电脑报的频繁 windows频率较少
  (原因是mac本在重启服务端的时候,服务端的地址并没有被释放掉,会导致服务端地址冲突)
  from socket import SOL_SOCKET,SO_REUSEADDR
  server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 在bind前加
  
3.链接循环
  如果是windows 客户端异常退出之后服务端会直接报错
  	处理方式:异常处理然后cuntinue跳出循环
    
  如果是mac或linux 服务端会接收到一个空消息
  	处理方式:len判断然后cuntinue跳出循环
    
  客户端如果异常断开 服务端代码应该重新回到accept等待新的客人

半连接池


listen(5)

在一个客户端连接入服务端时,其他客户端对服务端的连接将会处于半连接状态,半连接池就好像是一个容器,容器中的个体排队等待连接。

括号中的数字表示着,最大等待人数

黏包

粘包问题主要出现在用TCP协议传输中才会出现的问题,UDP不会出现,因为TCP传输中他会服务端会一次性把所有东西一并丢入缓存区,而读取的内容大小有时候没法准确的做到一一读取,所有会存在粘包。


缓冲区的作用:存储少量数据

如果你的网络出现短暂的异常或波动,接受数据就会出现短暂的中断,影响你的下载或者上传的效率,但是缓冲区解决了上传下载的传输效率的问题,带来了粘包问题

所以我们知道了黏包的主要问题是对即将接受的数据文件不确定其是多大的

接下来这个模块将可以处理这个问题


struct模块


struct 模块可以将任意大小的数字转换成一个固定长度(可选择)的 bytes, 这个原理类似于 hash 算法, 不论内容多大, 最终的 hash 值长度不变, 不同的是 hash 算法是不可逆的, 而且传入的原材料可以是文本、字符串等许多数据类型, struct 可以反解出原来的数据

ps : struct 模块只能转换数字, 不能转换其他的数据类型

	import struct

  data1 = 'hello world!'
  print(len(data1))  # 12
  res1 = struct.pack('i', len(data1))  # 第一个参数是格式
  print(len(res1))  # 4
  ret1 = struct.unpack('i', res1)
  print(ret1)  # (12,)


  data2 = 'hello baby baby baby baby baby baby baby baby'
  print(len(data2))  # 45
  res2 = struct.pack('i', len(data2))
  print(len(res2))  # 4
  ret2 = struct.unpack('i', res2)
  print(ret2)  # (45,)

pack可以将任意长度的数字打包成固定长度
unpack可以将固定长度的数字解包成打包之前数据真实的长度

流程:

  1. 先将真实数据打包打包成固定长度的包
  2. 将固定长度的包发送给对方
  3. 收到包后通过解包获取真实的数据长度
  4. 使用真实的长度接收真实的数据 sock.recv(1024)

posted @ 2022-04-15 22:34  Eason辰  阅读(35)  评论(0)    收藏  举报