ToB企服应用市场:ToB评测及商务社交产业平台

标题: day18-网络编程(下) [打印本页]

作者: 灌篮少年    时间: 2024-5-18 00:03
标题: day18-网络编程(下)
1. OSI 7层模型



OSI的7层模型对于各人来说可能不太好理解,所以我们通过一个案例来讲解:

假设,你在欣赏器上输入了一些关键字,内部通过DNS找到对应的IP后,再发送数据时内部会做如下的事:

每一层各司其职,最终保证数据呈如今到用户手中。
简单的可以理解为发快递:将数据外面套了7个箱子,最终用户收到箱子时必要打开7个箱子才气拿到数据。而在运输的过程中有些箱子是会被拆开并替换的,比方:
  1. 最终运送目标:上海 ~ 北京(中途可能需要中转站),在中转站会会打开箱子查看信息,在进行转发。
  2.         - 对于二级中转站(二层交换机):拆开数据链路层的箱子,查看mac地址信息。
  3.         - 对于三级中转站(路由器或三层交换机):拆开网络层的箱子,查看IP信息。
复制代码
在开发过程中其实只能表现:应用层、表现层、会话层、传输层,其他层的处理都是在网络装备中自动完成的。
  1. import socket
  2. client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  3. client.connect(('110.242.68.3', 80)) # 向服务端发送了数据包
  4. key = "你好"
  5. # 应用层
  6. content = "GET /s?wd={} http1.1\r\nHost:www.baidu.com\r\n\r\n".format(key)
  7. # 表示层
  8. content = content.encode("utf-8")
  9. client.sendall(content)
  10. result = client.recv(8196)
  11. print(result.decode('utf-8'))
  12. # 会话层 & 传输层
  13. client.close()
复制代码
2. UDP和TCP协议

协议,其实就是规定 连接、收发数据的一些规定。
在OSI的 传输层 除了界说端口信息以外,常见的还可以指定UDP或TCP的协议,协议差别连接和传输数据的细节也会差别。
2.1 UDP和TCP 示例代码

UDP示比方下:
TCP示比方下:
2.2 TCP三次握手和四次挥手

这是一个常见的面试题。

  1.     0                   1                   2                   3
  2.     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  3.    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  4.    |          Source Port          |       Destination Port        |
  5.    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  6.    |                        Sequence Number                        |
  7.    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  8.    |                    Acknowledgment Number                      |
  9.    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  10.    |  Data |           |U|A|P|R|S|F|                               |
  11.    | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
  12.    |       |           |G|K|H|T|N|N|                               |
  13.    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  14.    |           Checksum            |         Urgent Pointer        |
  15.    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  16.    |                    Options                    |    Padding    |
  17.    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  18.    |                             data                              |
  19.    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
复制代码
网络中的双方想要基于TCP连接举行通讯,必须要经过:
<ul>创建连接,客户端和服务端要举行三次握手。
  1. # 服务端
  2. import socket
  3. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  4. sock.bind(('127.0.0.1', 8001))
  5. sock.listen(5)
  6. while True:
  7.     conn, addr = sock.accept() # 等待客户端连接
  8.     ...
复制代码
  1. # 客户端
  2. import socket
  3. client = socket.socket()
  4. client.connect(('127.0.0.1', 8001)) # 发起连接
复制代码
  1.       客户端                                                服务端
  2.   1.  SYN-SENT    --> <seq=100><CTL=SYN>               --> SYN-RECEIVED
  3.   2.  ESTABLISHED <-- <seq=300><ack=101><CTL=SYN,ACK>  <-- SYN-RECEIVED
  4.   3.  ESTABLISHED --> <seq=101><ack=301><CTL=ACK>       --> ESTABLISHED
  5.       
  6. At this point, both the client and server have received an acknowledgment of the connection. The steps 1, 2 establish the connection parameter (sequence number) for one direction and it is acknowledged. The steps 2, 3 establish the connection parameter (sequence number) for the other direction and it is acknowledged. With these, a full-duplex communication is established.
复制代码
传输数据
  1. 在收发数据的过程中,只有有数据的传送就会有应答(ack),如果没有ack,那么内部会尝试重复发送。
复制代码
关闭连接,客户端和服务端要举行4次挥手。
  1. import socket
  2. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  3. sock.bind(('127.0.0.1', 8001))
  4. sock.listen(5)
  5. while True:
  6.     conn, addr = sock.accept()
  7.         ...
  8.     conn.close() # 关闭连接
  9. sock.close()
复制代码
  1. import socket
  2. client = socket.socket()
  3. client.connect(('127.0.0.1', 8001))
  4. ...
  5. client.close() # 关闭连接
复制代码
  1.        TCP A                                                TCP B
  2.   1.  FIN-WAIT-1  --> <seq=100><ack=300><CTL=FIN,ACK>  --> CLOSE-WAIT
  3.   2.  FIN-WAIT-2  <-- <seq=300><ack=101><CTL=ACK>      <-- CLOSE-WAIT
  4.   3.  TIME-WAIT   <-- <seq=300><ack=101><CTL=FIN,ACK>  <-- LAST-ACK
  5.   4.  TIME-WAIT   --> <seq=101><ack=301><CTL=ACK>      --> CLOSED
复制代码
  1. # socket客户端(发送者)
  2. import socket
  3. client = socket.socket()
  4. client.connect(('127.0.0.1', 8001))
  5. client.sendall('alex正在吃'.encode('utf-8'))
  6. client.sendall('翔'.encode('utf-8'))
  7. client.close()
  8. # socket服务端(接收者)
  9. import socket
  10. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  11. sock.bind(('127.0.0.1', 8001))
  12. sock.listen(5)
  13. conn, addr = sock.accept()
  14. client_data = conn.recv(1024)
  15. print(client_data.decode('utf-8'))
  16. conn.close()
  17. sock.close()
复制代码
  1. # socket客户端(发送者)
  2. import socket
  3. client = socket.socket()
  4. client.connect(('127.0.0.1', 8001))
  5. client.sendall('alex正在吃'.encode('utf-8'))
  6. client.sendall('翔'.encode('utf-8'))
  7. client.close()
  8. # socket服务端(接收者)
  9. import socket
  10. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  11. sock.bind(('127.0.0.1', 8001))
  12. sock.listen(5)
  13. conn, addr = sock.accept()
  14. client_data = conn.recv(1024)
  15. print(client_data.decode('utf-8'))
  16. conn.close()
  17. sock.close() # 与服务端断开连接(四次挥手),默认会向服务端发送空数据。
复制代码
IO多路复用 + 非阻塞,可以实现让TCP的客户端同时发送多个请求,比方:去某个网站发送下载图片的请求。
  1. import socket
  2. import struct
  3. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  4. sock.bind(('127.0.0.1', 8001))
  5. sock.listen(5)
  6. conn, addr = sock.accept()
  7. # 固定读取4字节
  8. header1 = conn.recv(4)
  9. data_length1 = struct.unpack('i', header1)[0] # 数据字节长度 21
  10. has_recv_len = 0
  11. data1 = b""
  12. while True:
  13.     length = data_length1 - has_recv_len
  14.     if length > 1024:
  15.         lth = 1024
  16.     else:
  17.         lth = length
  18.     chunk = conn.recv(lth) # 可能一次收不完,自己可以计算长度再次使用recv收取,直到收完为止。 1024*8 = 8196
  19.     data1 += chunk
  20.     has_recv_len += len(chunk)
  21.     if has_recv_len == data_length1:
  22.         break
  23. print(data1.decode('utf-8'))
  24. # 固定读取4字节
  25. header2 = conn.recv(4)
  26. data_length2 = struct.unpack('i', header2)[0] # 数据字节长度
  27. data2 = conn.recv(data_length2) # 长度
  28. print(data2.decode('utf-8'))
  29. conn.close()
  30. sock.close()
复制代码
基于 IO多路复用 + 非阻塞的特性,无论编写socket的服务端和客户端都可以提升性能。其中
注意:IO多路复用只能用来监听 IO对象 是否发生变革,常见的有:文件是否可读写、电脑终端装备输入和输出、网络请求(常见)。
在Linux操作系统化中 IO多路复用 有三种模式,分别是:select,poll,epoll。(windows 只支持select模式)
监测socket对象是否新连接到来 or 新数据到来。
  1. import socket
  2. import struct
  3. client = socket.socket()
  4. client.connect(('127.0.0.1', 8001))
  5. # 第一条数据
  6. data1 = 'alex正在吃'.encode('utf-8')
  7. header1 = struct.pack('i', len(data1))
  8. client.sendall(header1)
  9. client.sendall(data1)
  10. # 第二条数据
  11. data2 = '翔'.encode('utf-8')
  12. header2 = struct.pack('i', len(data2))
  13. client.sendall(header2)
  14. client.sendall(data2)
  15. client.close()
复制代码
增补:socket + 非阻塞+ IO多路复用(IO操作对象都可以监测 + 文件)。
总结

作业(模块大作业)

请基于TCP协议实现一个网盘系统,包含客户端、服务端,各自需求如下:
参考代码见:https://files.cnblogs.com/files/blogs/814578/网盘系统(参考代码).zip?t=1713753389&download=true

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4