soket编程
socket编程
socket是基于C/S架构的,也就是说进行socket网络编程,需要编写两个py文件,一个服务端,一个客户端
Python中的socket通信逻辑(图片来自网络):
Python提供了两个级别访问网络服务:
- 低级别的网络服务支持基于Socket,它提供了标准的BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
- 高级别的网络服务模块SocketServer,它提供了服务器中心类,可以简化网络服务器的开发。
什么是Socket?
Socket又称套接字,应用程序通常通过套接字向网络发出请求或者响应网络的请求,使主机间或者一台计算机上的进程间可以通讯。
Socket()函数
Python中,我们用socket()函数来创建套接字,语法格式如下:
socket.socket(family=-1, type=-1, proto=-1)
参数
- family:套接字家族可以使AF_UNIX或者AF_INET,默认为-1
- type:套接字类型可以根据是面向连接还是非连接分为SOCK_STREAM或SOCK_DGRAM,默认为-1
- protocl:一般默认为0
直接socket.socket(),则全部使用默认值。
Socket对象内置方法
注意事项:
- Python3以后,socket传递的都是bytes类型的数据,字符串需要先转换一下,string.encode()即可;另一端接收到的bytes数据想要转换字符串,只要bytes.decode()一下就可以。
- 在正常通信时,accept()和recv()方法都是阻塞。所谓的阻塞就是指程序会一直暂停,等待数据过来。
Socket编程思路:
服务端:
- 创建套接字,绑定套接字到本地IP与端口:socket.socket),s.bind()
- 开始监听连接:s.listen()
- 进入循环,不断接受客户端的连接请求,并返回(conn,address)二元元组,其中conn是一个通信对象,可以用来接收和发送数据。address是连接客户端的地址。:s.accept()
- 接收传来的数据,或者发送数据给对方:s.recv(),s.sendall()
- 传输完毕后,关闭套接字:s.close()
客户端:
- 创建套接字,连接服务器地址:socket.socket),s.connect()
- 连接后发送数据和接收数据:s.sendall(),s.recv()
- 传输完毕后,关闭套接字:s.close()
简单实例
服务器端:
tocket_server.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'tian'
__data__ = '2020/1/7 18:09'
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket() #创建套接字
sk.bind(ip_port) #绑定服务器地址
sk.listen(5) #监听连接请求,超过后排队
print('启动socket服务,等待客户端连接...')
conn,address = sk.accept() #等待连接,此处自动阻塞,conn是通信对象,address连接客户端的地址,样式ip+端口
while True: #一个死循环,直到客户端发送exit的信号,才关闭连接
clinet_data = conn.recv(1024).decode()#接收信息
if clinet_data == 'exit': #判断是否退出连接
exit("通信结束")
print("来自{0}的客户端向你发来消息:{1}".format(address,clinet_data))
conn.sendall('服务器端已经接收到你的消息'.encode())#回馈信息给客户端
conn.close() #关闭连接
客户端:
socket_clinet.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'tian'
__data__ = '2020/1/7 18:10'
import socket
ip_port = ('127.0.0.1',9999)
s = socket.socket() #创建套接字
s.connect(ip_port) #连接服务器
while True:
inp = input("请输入要发送的信息:").strip()
if not inp:continue #防止输入空信息,导致异常退出
s.sendall(inp.encode())#将输入的内容发送到服务器端
if inp == 'exit': #如果输入的是exit,表示断开连接
print("通信结束!")
break
server_reply = s.recv(1024).decode()#接收服务器端的返回
print(server_reply)
s.close() #关闭连接
运行服务器端、客户端.py文件,进行测试。
默认情况下创建的是单进程单线程,同时只能处理一个客户端请求,如果实现多客户端请求,那么需要使用多线程。
实现多客户端请求
使用Python内置的threading模块,配合socket模块创建多线程服务器。客户端代码不需要修改,复制多一份.py。
tocket_server.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'tian'
__data__ = '2020/1/7 18:09'
import socket
import threading #导入线程模块
def link_handler(link,client):
"""
该函数为线程需要执行的函数,负责具体的服务器和客户端之间的通信工作
:param link: 当前线程处理的连接
:param client: 客户端ip和端口信息,一个二元元组
:return: None
"""
print("服务器开始接收来自[{0}:{1}]的请求...".format(client[0],client[1]))
while True:
clinet_data = link.recv(1024).decode()
if clinet_data == "exit":
print("结束与[{0}:{1}]的通信...".format(client[0],client[1]))
break
print("来自[{0}:{1}]的客户端向你发来信息:{2}".format(client[0],client[1],clinet_data))
link.sendall('服务器已经收到你的消息'.encode())
link.close()
ip_port = ('127.0.0.1',9999)
sk = socket.socket() #创建套接字
sk.bind(ip_port) #绑定服务器地址
sk.listen(5) #监听连接请求
print('启动socket服务,等待客户端连接...')
while True: #一个死循环,直到客户端发送exit的信号,才关闭连接
conn,address = sk.accept() #等待连接,此处自动阻塞
t = threading.Thread(target=link_handler,args=(conn,address))
t.start()
客户端代码不变,复制多几份.py文件,打开进行测试;
socketserver编程
上面使用socket和threading模块实现了一个简单的多线程服务器。在非正式环境,随便使用还可以。但是如果要在生产环境中使用,那就不行。
Python为了满足我们对多线程网络服务器的需求,提供了socketserver模块。socketserver在内部使用IO多路复用以及多线程/进程机场,实现了并发处理多个客户端请求的socket服务端。每个客户端请求连接的服务器时,socketserver服务端都会
创建一个【线程】或者【进程】专门负责处理当前客户端的所有请求。
使用ThreadingTCPServer使用的例子:
服务器端:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'tian'
__data__ = '2020/1/8 11:37'
import socketserver
class MyServer(socketserver.BaseRequestHandler):
"""
必须继承socketserver.BaseRequestHandler类
"""
def handle(self):
conn = self.request #request里封装了所有请求的数据
conn.sendall('欢迎访问socketserver服务器!'.encode())
while True:
data = conn.recv(1024).decode()
if data == 'exit':
print("断开与{0}的连接!".format(self.client_address,))
break
print("来自{0}的客户端向你发来消息:{0}".format(self.client_address,data))
conn.sendall(('已收到你的消息:{0}'.format(data).encode()))
if __name__ == '__main__':
#创建一个多线程TCP服务器
server = socketserver.ThreadingTCPServer(('127.0.0.1',9999),MyServer)
print("启动socketserver服务器!")
#启动服务器,服务器一直保持运行状态
server.serve_forever()
客户端(与上面的没有怎么改变)
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.connect(ip_port)
# sk.settimeout(5)
data = sk.recv(1024).decode()
print('服务器端返回的:',data)
while True:
inp = input('你:').strip()
if not inp:continue
sk.sendall(inp.encode())
if inp == 'exit':
print("谢谢使用,88!")
data = sk.recv(1024).decode()
print('服务器:',data)
sk.close()
备注:复制多几个客户端运行进行测试;