我们在这篇文章中(用Socket建立简单的数据传输 | Trtyr’s Blog),简单的做到了单次通信,但是在实际中肯定不会这么见简单

首先就是要做到持续通信,而且有可能我们需要多个不同的客户端,然后发现,我们之前写的那个 “玩具” 根本行不通,

一. 多次通信

我们之前的程序,每次传输后,都会自动结束,所以我们要解决这个问题。

我们很容易能想到,加死循环,让它一直连接

先改客户端和先改服务端都一样。我们先改客户端玩玩

1. 客户端修改

我们先看之前的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 服务端IP:172.40.66.163  
# 客户端IP:192.168.40.129

import socket

client = socket.socket() # 默认TCP连接

client.connect(('172.40.66.163', 8001)) # 服务端IP,以及服务端开放的端口

# 向服务端发送消息
client.send('我是客户端'.encode()) # 这个 "我是客户端" 是个字符串,我们需要encode一下

data = client.recv(1024) # 接受服务端发来的消息

print(data.decode()) # 打印从服务端发来的消息

client.close() # 关闭

这里,连接应该是只有一次,但是我们想要能够一直发送信息,即我们的 send 函数在循环里头。

之后呢,因为是要多次发送,我们不能老是发送一个东西,我们可以加一个 input 来自定义输出内容

除此之外,我们还需要加一个能够关闭请求的过程,即我们输入特定字符时,可以进行关闭会话,加个 if 就行了。

比如我们想要通过输入 “baibai” 来关闭,条件就是

1
2
if data.decode() == "baibai"
break

2. 服务器端

原先的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 服务端IP:172.40.66.163  
# 客户端IP:192.168.40.129


# 服务端
import socket

# 创建了一个基于ipv4的,tcp协议的的socket对象
service = socket.socket() # 建立socket TCP连接,实例化为sock

service.bind(('172.40.66.163', 8001)) # 绑定本地(服务端)IP,以及对应的端口

''' 你也可以这样写 client.bind(('0.0.0.0', 8001))
0.0.0.0表示绑定服务端上所有网卡的IP
'''

service.listen(5) # 开启监听模式

# 接收客户端的连接,创建socket连接对象,并且返回客户端的连接地址信息
connection, address = service.accept() # 通过accept来等待客户端的信息

data = connection.recv(1024) # 接收客户端发送的消息,1024指每次接收数据量的大小

# 输出客户端发来的消息
print(data.decode()) # 使用decode是因为通过sock传入的是bytes类型的数据,需要解码才能正确显示

# 向客户端发送消息
connection.send('我是服务端'.encode()) # 这个 "我是服务端" 是个字符串,我们需要encode一下

connection.close()

和客户端同理,把send函数套进循环,然后加自定义输入语句

然后就是通信关闭的问题,还是一样加个 if 判断就行

3. 实例代码

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 服务端IP:172.40.66.163  
# 客户端IP:192.168.40.129


# 服务端
import socket

# 创建了一个基于ipv4的,tcp协议的的socket对象
service = socket.socket() # 建立socket TCP连接,实例化为service

service.bind(('172.40.66.163', 8001)) # 绑定本地(服务端)IP,以及对应的端口

''' 你也可以这样写 client.bind(('0.0.0.0', 8001))
0.0.0.0表示绑定服务端上所有网卡的IP
'''

service.listen(5) # 开启监听模式

# 接收客户端的连接,创建socket连接对象,并且返回客户端的连接地址信息
connection, address = service.accept() # 通过accept来等待客户端的信息

# 加死循环,做到能够持续连接
while True:

data = connection.recv(1024) # 接收客户端发送的消息,1024指每次接收数据量的大小

if data.decode() == "baibai":
break
# 输出客户端发来的消息
print(f"来自客户端 {address} 的信息:{data.decode()}") # 使用decode是因为通过sock传入的是bytes类型的数据,需要解码才能正确显示

to_data = input("请输入:") # 自定义输入内容

# 向客户端发送消息
connection.send(to_data.encode()) # 发送 to_data 中的内容,内容是个字符串,我们需要encode一下

connection.close()
service.close()

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 服务端IP:172.40.66.163  
# 客户端IP:192.168.40.129

import socket

client = socket.socket() # 默认TCP连接

client.connect(('172.40.66.163', 8001)) # 服务端IP,以及服务端开放的端口

# 加死循环,做到能够持续连接
while True:
to_data = input("请输入:") # 自定义输入内容

# 向服务端发送消息
client.send(to_data.encode()) # 发送 to_data 中的内容,内容是个字符串,我们需要encode一下

data = client.recv(1024) # 接受服务端发来的消息

if data.decode() == "baibai":
break
print(data.decode()) # 打印从服务端发来的消息

client.close()

4. 测试

(1) 测试 - 1

客户机发送 baibai

9349b72262715069.webp

(2) 测试 - 2

服务端发送baibai

b1165621689cc6ae.webp

5. 问题

可以发现,在整个通信过程中,有很多不同的事件,这些事件需要我们自己处理

比如上面的,断开之后没反应。

如果没有处理的话,容易发生异常

我们问题先放在这里

二. 多客户端连接

1. 代码

当前的服务端仅支持一个服务端对应一个客户端,这里自己试试就行了,我就不演示了。

那么怎么解决这个问题

我们接受客户端的请求是这句话

1
2
# 接收客户端的连接,创建socket连接对象,并且返回客户端的连接地址信息  
connection, address = service.accept() # 通过accept来等待客户端的信息

你可能会想,把这玩意放循环里头不就得了嘛。肯定不行哈,不用想都知道,新的会话会覆盖掉旧的会话,得到一个全新的连接对象,之前的那个就死掉了。

而且还会出现话说一半断开的情况,因为在经过 accept() 它就直接阻塞在这里了,没法往下走了,就直接死掉了

不过加循环是必须的,但是我们要处理一下

就改服务端就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 服务端IP:172.40.66.163  
# 客户端IP:192.168.40.129
# 客户端IP:192.168.40.142


# 服务端
import socket
import threading

# 创建了一个基于ipv4的,tcp协议的的socket对象
service = socket.socket() # 建立socket TCP连接,实例化为service

service.bind(('172.40.66.163', 8001)) # 绑定本地(服务端)IP,以及对应的端口

''' 你也可以这样写 client.bind(('0.0.0.0', 8001))
0.0.0.0表示绑定服务端上所有网卡的IP
'''

service.listen(5) # 开启监听模式


def handel_socket(con, addr):
while True:
data = connection.recv(1024) # 接收客户端发送的消息,1024指每次接收数据量的大小
if data.decode() == "baibai":
break
to_data = input("请输入:") # 自定义输入内容

# 向客户端发送消息
connection.send(to_data.encode()) # 发送 to_data 中的内容,内容是个字符串,我们需要encode一下

print(f"来自客户端 {address} 的信息:{data.decode()}") # 使用decode是因为通过sock传入的是bytes类型的数据,需要解码才能正确显示


# 加死循环,做到能够持续连接
while True:
# 接收客户端的连接,创建socket连接对象,并且返回客户端的连接地址信息
connection, address = service.accept() # 通过accept来等待客户端的信息

client_thread = threading.Thread(target=handel_socket, args=(connection, address))
# 输出客户端发来的消息

client_thread.start()

2. 实操

700dd1c8ee2d60d9.webp

能看出来,能够实现通信。但是又出现了一个问题。

我们发现 客户机1 对服务端发起会话的时候,服务端回复时,只是针对 客户端1 进行回复;客户端2 对服务端进行会话的时候,服务端只是对 客户端2 进行回复。

但是在 客户端1 发起请求的时候,我不进行回复。然后 客户端2 发起会话请求,我进行回复,这个时候就出现了BUG,我回复的是 客户端2,那么 客户端1 的会话被搁置了,即服务端现在无法对 客户端1 进行回复了。