套接字介绍
1.套接字 : 实现网络编程进行数据传输的一种技术手段
2.Python实现套接字编程:import socket
3.套接字分类
- 流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案。(面向连接--tcp协议--可靠的--流式套接字)
- 数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案。(无连接--udp协议--不可靠--数据报套接字)
tcp套接字
服务端流程
1.创建套接字
sockfd=socket.socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0)
- 功能:创建套接字
- 参数: socket_family 网络地址类型 AF_INET表示ipv4
- socket_type 套接字类型 SOCK_STREAM(流式) SOCK_DGRAM(数据报)
- proto 通常为0 选择子协议
- 返回值: 套接字对象
2.绑定地址
本地地址 : 'localhost' , '127.0.0.1'
网络地址 : '172.40.91.185'
自动获取地址: '0.0.0.0'
sockfd.bind(addr)
- 功能: 绑定本机网络地址
- 参数: 二元元组 (ip,port) ('0.0.0.0',8888)
3.设置监听
sockfd.listen(n)
- 功能 : 将套接字设置为监听套接字,确定监听队列大小
- 参数 : 监听队列大小
4.等待处理客户端连接请求
connfd,addr = sockfd.accept()
- 功能: 阻塞等待处理客户端请求
- 返回值: connfd 客户端连接套接字
- addr 连接的客户端地址
5.消息收发
data = connfd.recv(buffersize)
- 功能 : 接受客户端消息
- 参数 :每次最多接收消息的大小
- 返回值: 接收到的内容
n = connfd.send(data)
- 功能 : 发送消息
- 参数 :要发送的内容 bytes格式
- 返回值: 发送的字节数
6.关闭套接字
sockfd.close()
- 功能:关闭套接字
tcp服务端流程
客户端流程
1.创建套接字
注意:只有相同类型的套接字才能进行通信
2.请求连接
sockfd.connect(server_addr)
- 功能:连接服务器
- 参数:元组 服务器地址
3.收发消息
注意: 防止两端都阻塞,recv send要配合
4.关闭套接字
1 """
2 重点代码
3 """
4
5 from socket import *
6
7 # 创建tcp套接字
8 sockfd = socket() # 参数默认即tcp套接字
9
10 # 连接服务端程序
11 server_addr = ("172.40.91.150",8888) # 服务端地址
12 sockfd.connect(server_addr)
13
14 while True:
15 # 消息发送接收
16 data = input("Msg>>")
17 # 如果直接回车,则跳出循环
18 if not data:
19 break
20 sockfd.send(data.encode()) # 转换字节串发送
21 data = sockfd.recv(1024)
22 print("Server:",data.decode())
23
24 sockfd.close()
tcp客户端流程
tcp 套接字数据传输特点
- tcp连接中当一端退出,另一端如果阻塞在recv,此时recv会立即返回一个空字串。
- tcp连接中如果一端已经不存在,仍然试图通过send发送则会产生BrokenPipeError
- 一个监听套接字可以同时连接多个客户端,也能够重复被连接
网络收发缓冲区
- 网络缓冲区有效的协调了消息的收发速度
- send和recv实际是向缓冲区发送接收消息,当缓冲区不为空recv就不会阻塞。
tcp粘包
代码示例:day2/stick_send.py,stick_recv.py
原因:tcp以字节流方式传输,没有消息边界。多次发送的消息被一次接收,此时就会形成粘包。
影响:如果每次发送内容是一个独立的含义,需要接收端独立解析此时粘包会有影响。
处理方法:
- 人为的添加消息边界
- 控制发送速度
1 from socket import *
2
3
4 sockfd = socket()
5
6 server_addr = ("172.40.91.150",8888)
7 sockfd.connect(server_addr)
8
9 while True:
10 sockfd.send(b'hello')
11
12 sockfd.close()
tcp 粘包
1 import socket
2
3 sockfd = socket.socket(socket.AF_INET,
4 socket.SOCK_STREAM)
5 sockfd.bind(('0.0.0.0', 8888))
6
7 sockfd.listen(3)
8
9 while True:
10 print("Waiting for connect ...")
11 connfd, addr = sockfd.accept()
12 print("Connect from", addr)
13
14 n = 0
15 while n < 10:
16 n += 1
17 data = connfd.recv(5)
18 print(data)
19
20 connfd.close() # 断开连接
21
22 # 关闭套接字
23 sockfd.close()
tcp粘包问题
练习:将一个文件从客户端发送到服务端,要求文件类型随意.
思路:读取文件--> send发送 recv接收--> write写入
1 from socket import *
2 import time
3 # 读取文件--> send发送
4 s = socket()
5 s.connect(('127.0.0.1',8888))
6
7 f = open('img.jpg','rb')
8
9 # 读取内容,将其发送
10 while True:
11 data = f.read(1024)
12 if not data:
13 time.sleep(0.1)
14 s.send(b'##')
15 break
16 s.send(data)
17
18 time.sleep(0.1)
19 s.send("发送完毕".encode())
20
21 f.close()
22 s.close()
23 ---------------------------------------------
24 from socket import *
25
26 s = socket()
27 s.bind(('0.0.0.0',8888))
28 s.listen(3)
29
30 c,addr = s.accept()
31 print("Connect from",addr)
32
33 # 以二进制写入
34 f = open('mm.jpg','wb')
35
36 #循环接收内容,写入文件
37 while True:
38 # recv接收--> write写入
39 data = c.recv(1024)
40 if data == b'##':
41 break
42 f.write(data)
43
44 data = c.recv(1024)
45 print(data.decode())
46
47 f.close()
48 c.close()
49 s.close()
练习