并发与并行的区别:(来自百度百科)
并发:并发当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。
并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
区别:并发和并行是即相似又有区别的两个概念,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。倘若在计算机系统中有多个处理机,则这些可以并发执行的程序便可被分配到多个处理机上,实现并行执行,即利用每个处理机来处理一个可并发执行的程序,这样,多个程序便可以同时执行。
以上来自于百度百科,简单来说,并发和并行在宏观上都体现为多个线程同时运行。若这些线程运行在单核处理器上,则这些线程只能交替运行,此时为并发;若这些线程运行在多核处理器上,此时每个线程可以占用一个处理器,这些线程同时运行,叫做并行。
利用socketserver实现server多并发
(关于socketserver的详细讲解,见官方文档:21.21. socketserver — A framework for network servers)
socketserver是python中一个简化编写server的模块。包含四个具体的类:
- class
socketserver.
TCPServer
(server_address, RequestHandlerClass, bind_and_activate=True): This uses the Internet TCP protocol, which provides for continuous streams of data between the client and server. If bind_and_activate is true, the constructor automatically attempts to invokeserver_bind()
andserver_activate()
. The other parameters are passed to theBaseServer
base class. 这个类使用TCP协议,能够在客户和服务器之间提供连续的数据流。如果bind_and_activate为true,则构造函数会自动尝试调用server_bind()和server_activate()。 其他参数将传递给BaseServer基类。 -
class
socketserver.
UDPServer
(server_address, RequestHandlerClass, bind_and_activate=True)This uses datagrams, which are discrete packets of information that may arrive out of order or be lost while in transit. The parameters are the same as for
TCPServer
. 这个类使用UDP协议。 -
class
socketserver.
UnixStreamServer
(server_address, RequestHandlerClass, bind_and_activate=True) -
class
socketserver.
UnixDatagramServer
(server_address, RequestHandlerClass, bind_and_activate=True) These more infrequently used classes are similar to the TCP and UDP classes, but use Unix domain sockets; they’re not available on non-Unix platforms. The parameters are the same as forTCPServer
. 第三个类和第四个类与前面1、2所述的TCP、UDP类类似,但之使用在Unix平台上。
如何使用socketserver
创建一个socketserver至少需要以下几步:
- 我们必须要创建一个请求处理类,并且这个类要继承BaseRequestHandler,并且还要重写父类中的Handler()方法;
- 我们实例化一个实例(上面四个中的其中一个),并且传递server ip和上面创建的请求处理类给这个实例;
- 调用调用handle_request() 或者serve_forever()模块。其中: server.handle_request() # 只处理一个请求 server.handle_forever() # 处理多个请求
- 关闭server(server.close())
服务器端代码示例:
import socketserver
from bs4 import BeautifulSoup
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
while True:
try:
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
self.request.send(self.data.upper())
except ConnectionResetError as e:
print("err",e)
break
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
客户端:
__author__ = "Scott Knight"
import socket
client = socket.socket()
#client.connect(('192.168.16.200',9999))
client.connect(('localhost',9999))
while True:
cmd = input(">>:").strip()
if len(cmd) == 0: continue
client.send(cmd.encode("utf-8"))
cmd_res_size = client.recv(1024) ##接受命令结果的长度
print("命令结果大小:",cmd_res_size)
received_size = 0
received_data = b''
while received_size < int(cmd_res_size.decode()) :
data = client.recv(1024)
received_size += len(data) #每次收到的有可能小于1024,所以必须用len判断
#print(data.decode())
received_data += data
else:
print("cmd res receive done...",received_size)
print(received_data.decode())
client.close()
总结:查看socketserver源码时发现,请求处理的基类是BaseRequestHandler,其中一般需要重写的方法就是handle方法,主要就是如何处理接下来的请求,在这个类里,主要有三个方法,一个是setup,handle和finish方法,在调用这个类的时候,先调用setup进行一些初始化的工作,然后调用handle方法进行处理请求,然后调用finish方法,做一些关闭连接什么的;在这个里面最主要的参数self.request,也就是请求的socket对象,其中可以发送消息sendall或者send,接收消息的recv。
在请求处理的子类中有两个,一个是SreamRequestHandle和DatagramRequestHandle,在这个里面重写了基类的setup方法和finish方法,handle方法没有重写,因为这个是留给用户做处理请求的方法,在这里提供了几个参数,一个self.rfile用来读取数据的句柄,而self.wfile是用来发送消息的句柄。