利用python实现socket通信,需要实现客户端和服务端。通常,服务端不仅仅是为一个客户端服务,可能同时为多个客户端提供连接服务,在python中常见的提供多连接的方式有进程、线程、协程以及select和poll实现的异步I/O。这篇文章,主要介绍使用进程、线程、协程实现的socket多连接通信。
首先,这里先介绍一下进程、线程、协程的区别?(https://my.oschina.net/u/4543837/blog/4465255)
进程:操作系统提供的抽象概念,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。程序本身是没有生命周期的,它只是存在磁盘上的一些指令,程序一旦运行就是进程。
线程:也是操作系统提供的抽象概念,是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。
协程(Coroutine,又称微线程)是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制。协程与线程以及进程的关系见下图所示。
-
协程可以比作子程序,但执行过程中,子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。协程之间的切换不需要涉及任何系统调用或任何阻塞调用
-
协程只在一个线程中执行,是子程序之间的切换,发生在用户态上。而且,线程的阻塞状态是由操作系统内核来完成,发生在内核态上,因此协程相比线程节省线程创建和切换的开销
-
协程中不存在同时写变量冲突,因此,也就不需要用来守卫关键区块的同步性原语,比如互斥锁、信号量等,并且不需要来自操作系统的支持。
协程适用于IO阻塞且需要大量并发的场景,当发生IO阻塞,由协程的调度器进行调度,通过将数据流yield掉,并且记录当前栈上的数据,阻塞完后立刻再通过线程恢复栈,并把阻塞的结果放到这个线程上去运行。
进程实现tcp socket通信
server
-
import socket
-
from multiprocessing
import Process
-
-
-
class ServerProcess:
-
def __init__(self, ipaddr, port, num):
-
self.ipaddr = ipaddr
-
self.port = port
-
self.num = num
-
-
# 服务端的数据接收,在调用时使用多进程
-
def server_link(self, conn, addr):
-
conn.send(
"Welcome connect!".encode())
-
-
while
True:
-
try:
-
data = conn.recv(
1024)
-
if data:
-
print(
"from {0}:".format(addr), data.decode(
'utf-8'))
-
else:
-
break
-
except Exception:
-
break
-
-
conn.close()
-
-
# 服务端的启动程序
-
def server_start(self):
-
# IPv4
-
s_pro = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
# 操作系统会在服务器socket被关闭或服务器进程终止后马上释放该服务器的端口
-
s_pro.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1)
-
s_pro.bind((self.ipaddr, self.port))
-
s_pro.listen(self.num)
-
print(
'Waiting link...')
-
while
True:
-
conn, addr = s_pro.accept()
-
print(
"Success connect from ", addr)
-
# 启动多进程实现多连接
-
p = Process(target=self.server_link, args=(conn, addr))
-
p.start()
-
-
-
if __name__ ==
'__main__':
-
server = ServerProcess(
'127.0.0.1',
12345,
5)
-
server.server_start()
线程实现tcp socket通信
server
-
import socket
-
import threading
-
-
-
class ServerThread:
-
def __init__(self, ipaddr, port, num):
-
self.ipaddr = ipaddr
-
self.port = port
-
self.num = num
-
-
def server_link(self, conn, addr):
-
conn.send(
"Welcome connect!".encode())
-
-
while
True:
-
try:
-
data = conn.recv(
1024)
-
if data:
-
print(
"from {0}:".format(addr), data.decode(
'utf-8'))
-
else:
-
break
-
except Exception:
-
break
-
-
conn.close()
-
-
def server_start(self):
-
s_pro = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
s_pro.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1)
-
s_pro.bind((self.ipaddr, self.port))
-
s_pro.listen(self.num)
-
print(
'Waiting link...')
-
while
True:
-
conn, addr = s_pro.accept()
-
print(
"Success connect from ", addr)
-
p = threading.Thread(target=self.server_link, args=(conn, addr))
-
p.start()
-
-
-
if __name__ ==
'__main__':
-
server = ServerThread(
'127.0.0.1',
12345,
5)
-
server.server_start()
协程实现tcp socket通信
server
-
import socket
-
import asyncio
-
-
-
class ServerProcess:
-
def __init__(self, ipaddr, port, num):
-
self.ipaddr = ipaddr
-
self.port = port
-
self.num = num
-
-
async
def server_link(self, conn, addr):
-
conn.send(
"Welcome connect!".encode())
-
-
while
True:
-
try:
-
data = conn.recv(
1024)
-
if data:
-
print(
"from {0}:".format(addr), data.decode(
'utf-8'))
-
else:
-
break
-
except Exception:
-
break
-
-
conn.close()
-
-
def server_start(self):
-
s_pro = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
s_pro.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1)
-
s_pro.bind((self.ipaddr, self.port))
-
s_pro.listen(self.num)
-
print(
'Waiting link...')
-
while
True:
-
conn, addr = s_pro.accept()
-
print(
"Success connect from ", addr)
-
loop = asyncio.get_event_loop()
-
loop.run_until_complete(self.server_link(conn, addr))
-
-
-
if __name__ ==
'__main__':
-
server = ServerProcess(
'127.0.0.1',
12345,
5)
-
server.server_start()
对于以上三种方式实现的服务端,客户端这里我都采用同样的代码,可以写多个客户端测试socket通信的并发性。
client
-
import socket
-
-
-
class Client1:
-
def __init__(self, ipaddr, port):
-
self.ipaddr = ipaddr
-
self.port = port
-
-
def client_link(self):
-
c1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
c1.connect((self.ipaddr, self.port))
-
print(
'receive info:', c1.recv(
1024).decode(
'utf-8'))
-
while
True:
-
info = input(
'enter info:')
-
c1.send(info.encode())
-
-
-
if __name__ ==
'__main__':
-
c = Client1(
'127.0.0.1',
8888)
-
c.client_link()
转载:https://blog.csdn.net/weixin_40042248/article/details/115337135