为单用户服务
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
| import socket
ip_port = ('127.0.0.1', 5555)
s = socket.socket()
s.bind(ip_port)
s.listen(5)
conn, addr = s.accept()
while True:
recv_data = conn.recv(1024)
if len(recv_data) == 0: break
send_data = recv_data.upper() conn.send(send_data)
conn.close()
|
这个服务端在与客户端交互(收发消息)的部分使用了循环,没次接收消息,处理消息,发送消息之后就进入到下一次循环等待接收消息。
这里需要注意的一点就是conn,服务端的conn对象对应了客户端的s对象,都代表了两端之间建立的连接通道,一旦客户端主动关闭连接对象s,在服务端对应的conn对象将立即失效,随即退出循关闭服务端持有的连接对象。
此时conn.recv()函数不再是一个阻塞的状态,它将返回空值。我们需要对接收这个空值的对象(变量)做出相应的处理,关闭掉服务端持有的连接对象
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
| import socket
ip_port = ('127.0.0.1', 5555)
s = socket.socket()
s.connect(ip_port)
while True:
send_data = input("> ").strip()
if send_data == "exit": break
if len(send_data) == 0: continue
s.send(bytes(send_data, encoding='utf-8'))
recv_data = s.recv(1024) print(recv_data, "--->", type(recv_data), str(recv_data, encoding='utf-8'))
s.close()
|
这个版本实现了服务端为单个用户提供服务的场景。
从上面的说明中也可以看出,服务端在接收到客户端主动关闭的操作后(特征为:conn.recv返回值为空),相继关闭服务端持有的连接对象,接着代码就停止了。
这里需要特别注意的就是处理客户端输入为空的情况,因为服务端判断客户端是否主动断开,主要依据的就是conn.recv方法的返回值是否为空,所以我们要屏蔽掉用户输入为空的情况
为多用户服务(排队)
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
| import socket
ip_port = ('127.0.0.1', 5555)
s = socket.socket()
s.bind(ip_port)
s.listen(5)
while True:
conn, addr = s.accept()
while True:
recv_data = conn.recv(1024)
if len(recv_data) == 0: break
send_data = recv_data.upper() conn.send(send_data)
conn.close()
|
服务端的代码只增加了一个while循环,上一个代码版本中,服务端唯一的循环放在了处理某一客户端的请求上。此次加上的循环,放在了接收用户连接请求上,可以在关闭上一个连接之后
,循环的处理下一个连接请求
此时,连续的运行两个客户端,发现两个客户端都可以连接到服务端,但是一个可断端发出请求后,在第一个客户端不关闭连接的情况下,第二个客户端发出的请求一直卡在终端中。只有第一个客户端关闭连接,触发服务端关闭对第一个客户端的连接之后,才会去接收第二个客户端发来的信息
这里特别提示一下,当两个客户端相继连接上服务端后,看似服务端对两个连接都接受了请求,但是,只有第一个连接过来的客户端才进入到了服务端代码中的内层while循环体,此时即使第一个客户端没有发送信息,第二个客户端先给服务端发送信息,终端也是会阻塞住的