Python基础之网络编程:3、socket模块
Python基础之socket模块
一、socket模块简介
在以往学习的编程中,我们所编写的代码都是只能在自己的电脑中使用,无法通过网络进行交互,如果我们需要编写基于网络进行交互的程序,就意味着我们需要通过代码来控制我们之前所学习的七层协议(繁琐、复杂、类似于自己编写操作系统)
socket类似于操作系统,封装了丑陋复杂的接口,提供简单、快捷的接口
- socket也叫套接字
- 基于文件类型的套接字家族(单机)
- AF_UNIX
- 基于网络类型的套接字家族(联网)
- AF_INET
- AF_INET
- 基于文件类型的套接字家族(单机)
二、代码实现
1、服务端
# 导入socket模块
import socket
# 1、产生一个socket对象,并指定采用的通信版本和版本协议(TCP)
# 括号内不填写 默认采用TCP,family=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP
server_ojb = socket.socket()
# 2、绑定一个固定的地址(服务端必备条件)元组内第二位是指定端口
server_ojb.bind(('192.168.1.188', 8989))
# 3、设立半连接池
server_ojb.listen(5)
# 4、等待客户端接入---此处内部涉及’三次握手‘
sock, address = server_ojb.accept()
# 5、接收到客户端、指定接收字节数
data = sock.recv(1024)
print(data.decode('utf8'))
# 6、接收到客户端信息,反馈信息---只能支持二进制,字符需要转码
sock.send('欢迎光临~~'.encode('utf8'))
# 7、关闭通道
sock.close()
# 8、关闭整个程序
server_ojb.close()
2、客户端
# 1、导入模块
import socket
# 2、指定客户端对象
client_obj = socket.socket()
# 3、通过服务端的地址进行链接
client_obj.connect(('192.168.1.188', 8989))
# 4、直接给客户端发送消息
client_obj.send('店小二!快把好酒好菜给爷端上来!...'.encode('utf8'))
# 5、接收服务端发送过来的消息,并指定文件大小
data = client_obj.recv(1024)
print(data.decode('utf8'))
# 6、断开与服务端的链接
三、代码优化
通过以上代码,可以实现同局域网下用户交互功,但是任然存在很多问题,下面键沟通过优化实现以下功能
1、聊天内容自定义
正对消息,采用input格式化输入
2、循环聊天
将聊天部分使用循环封起来
3、用户输入信息不能为空
当用户输入空消息时,另一端无法接收到空消息,这时两方将会陷入等待之 中,可以使用if判断决绝问题
4、服务端多次重启会报错问题
方式一:更改端口号
方式二:导入模块,主要针对于苹果电脑问题
5、当客户主动断开如何继续服务其他客户端
可以采用异常捕获,当异常时重新回到循环处判定
1、服务端
# 1、导入模块
import socket
# 2、指定客户端对象
client_obj = socket.socket()
# 3、通过服务端的地址进行链接
while True:
try:
client_obj.connect(('192.168.1.188', 8898))
except OSError:
print('客户端已连接其他主机...请稍后在尝试连接')
break
except ConnectionRefusedError:
print('对方未开机或其他错误...正在尝试重新连接...')
else:
print('已连接至指定服务端')
while True:
# 4、直接给客户端发送消息
client_send = input('请输入需要回复的消息>>>:').encode('utf8')
if not client_send:
print('不能发送空消息...请重新输入')
continue
try:
client_obj.send(client_send)
except OSError:
print('由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。')
except ConnectionResetError:
print('消息发送失败...客户端已断开连接...')
break
else:
print('消息已发送...请耐心等待对方回复...')
# 5、接收服务端发送过来的消息,并指定文件大小
try:
data = client_obj.recv(1024)
print('收到客户端消息>>>:', data.decode('utf8'))
except OSError:
print('由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。')
except ConnectionResetError:
continue
2、客户端
# 1、导入模块
import socket
# 2、指定客户端对象
client_obj = socket.socket()
# 3、通过服务端的地址进行链接
while True:
try:
client_obj.connect(('192.168.1.99', 11888))
except OSError:
print('客户端已连接其他主机...请稍后在尝试连接')
break
except ConnectionRefusedError:
print('对方未开机或其他错误...正在尝试重新连接...')
else:
print('已连接至指定服务端')
while True:
# 4、直接给客户端发送消息
client_send = input('请输入需要回复的消息>>>:').encode('utf8')
if not client_send:
print('不能发送空消息...请重新输入')
continue
try:
client_obj.send(client_send)
except OSError:
print('由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。')
except ConnectionResetError:
print('消息发送失败...客户端已断开连接...')
break
else:
print('消息已发送...请耐心等待对方回复...')
# 5、接收服务端发送过来的消息,并指定文件大小
try:
data = client_obj.recv(1024)
print('收到客户端消息>>>:', data.decode('utf8'))
except OSError:
print('由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。')
except ConnectionResetError:
continue
四、半连接池的概念
server.listen(5) # 半连接池
当有多个客户端来链接的情况下 我们可以设置等待数量(不考虑并发问题)
假设服务端只有一个人的情况下
比如上面我们半连接池设置成五个,在优化后的代码中,由于用while循环包裹了代码,当我们在运行代码的时候可以这样理解,第一个客户端就会直接跟服务端进行交互,除此之外还有五个客户端可以排队,但是发出去的信息服务端那边暂时不会处理,要等到第一个客户端处理结束断开连接才会根据先后顺序依次往后处理
在测试半连接池的时候 可以不用input获取消息 直接把消息写死即可