python3之socket&socketserver网络编程
1、套接字与套接模块
套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象。它们允许程序接受并进行连接,如发送和接受数据。为了建立通信通道,网络通信的每个端点拥有一个套接字对象极为重要。
套接字为BSD UNIX系统核心的一部分,而且他们也被许多其他类似UNIX的操作系统包括Linux所采纳。许多非BSD UNIX系统(如ms-dos,windows,os/2,mac os及大部分主机环境)都以库形式提供对套接字的支持。
三种最流行的套接字类型是:stream,datagram和raw。stream和datagram套接字可以直接与TCP协议进行接口,而raw套接字则接口到IP协议。但套接字并不限于TCP/IP。
套接字模块是一个非常简单的基于对象的接口,它提供对低层BSD套接字样式网络的访问。使用该模块可以实现客户机和服务器套接字。要在python 中建立具有TCP和流套接字的简单服务器,需要使用socket模块。利用该模块包含的函数和类定义,可生成通过网络通信的程序。
python提供了两个级别访问的网络服务:
- 低级的网络服务支持基本的socket,它提供了标准的BSD sockets API,可以访问底层操作系统socket接口的全部方法
- 高级别的网络服务模块socketServer,它提供了服务器中心类,可以简化网络服务器的开发。
2、socket模块方法
socket.
socket(family = AF_INET,type = SOCK_STREAM,proto = 0,fileno = None ) :使用给定的地址系列,套接字类型和协议号创建一个新套接字
socket.
socketpair([ family [,type [,proto ] ] ] ):使用给定的地址系列,套接字类型和协议编号构建一对连接的套接字对象
socket.
create_connection(address [,timeout [,source_address ] ] ):连接到侦听Internet 地址(2元组 )的TCP服务,然后返回套接字对象
socket.
fromfd(fd,family,type,proto = 0 ):复制文件描述符fd(由文件对象的
fileno()方法返回的整数 ),并从结果中构建一个套接字对象
socket.
fromshare(data):从该
socket.share()方法获得的数据实例化一个套接字。假设套接字处于阻塞模式。
socket.
SocketType:这是表示套接字对象类型的Python类型对象。这是一样的type(socket(...))[code]socket.
getaddrinfo(host,port,family = 0,type = 0,proto = 0,flags = 0 ):将主机 / 端口参数转换为5元组序列,其中包含创建连接到该服务的套接字的所有必要参数
socket.
getfqdn([ name ] ):为名称返回完全限定的域名。如果名称被省略或为空,则被解释为本地主机
socket.
gethostbyname(hostname):将主机名转换为IPv4地址格式。IPv4地址以字符串形式返回
socket.
gethostbyname_ex(hostname):将主机名转换为IPv4地址格式,扩展接口。返回一个triple ,其中主机名是响应给定ip_address的主要主机名,aliaslist是同一地址的备用主机名(可能为空)列表,ipaddrlist是同一主机上同一接口的IPv4地址列表经常但不总是一个地址)。不支持IPv6名称解析,应该用于IPv4 / v6双栈支持
socket.
gethostname():返回包含Python解释器当前正在执行的机器的主机名的字符串
socket.
gethostbyaddr(ip_address ):返回一个triple ,其中hostname是响应给定ip_address的主要主机名,aliaslist是同一地址的备用主机名(可能为空)列表, ipaddrlist是同一个接口的IPv4 / v6地址列表主机(最有可能只包含一个地址)。要找到完全限定的域名,请使用该功能。支持IPv4和IPv6
socket.
getnameinfo(sockaddr,flags ):将套接字地址sockaddr翻译成2元组。根据标志的设置,结果可以在主机中包含完全限定的域名或数字地址表示。同样,端口可以包含字符串端口名称或数字端口号。
(host, port)
socket.
getprotobyname(protocolname ):将Internet协议名称(例如,
'icmp')转换为适合作为(可选)第三个参数传递给该
socket()函数的常量。这通常只需要以“原始”模式(
SOCK_RAW)打开的套接字; 对于正常的套接字模式,如果协议被省略或为零,则自动选择正确的协议
socket.
getservbyname(servicename [,protocolname ] ):将Internet服务名称和协议名称转换为该服务的端口号。可选的协议名称,如果有,应该是
'tcp'或
'udp',否则任何协议将匹配
socket.
getservbyport(port [,protocolname ] ):将Internet端口号和协议名称转换为该服务的服务名称。可选的协议名称,如果有,应该是
'tcp'或
'udp',否则任何协议将匹配
socket.
ntohl(x ):将32位正整数从网络转换为主机字节顺序。在主机字节顺序与网络字节顺序相同的机器上,这是无操作的; 否则,它会执行一个4字节的交换操作。
socket.
ntohs(x ):将16位正整数从网络转换为主机字节顺序。在主机字节顺序与网络字节顺序相同的机器上,这是无操作的; 否则,它执行一个2字节的交换操作
socket.
htonl
(x )将32位正整数从主机转换为网络字节顺序。在主机字节顺序与网络字节顺序相同的机器上,这是无操作的; 否则,它会执行一个4字节的交换操作。
socket.
htons
(x )将16位正整数从主机转换为网络字节顺序。在主机字节顺序与网络字节顺序相同的机器上,这是无操作的; 否则,它执行一个2字节的交换操作
socket.
inet_aton(ip_string ):将IPv4地址从点分四字符串格式(例如“123.45.67.89”)转换为32位打包二进制格式,作为长度为4个字符的字节对象。当与使用标准C库的程序进行交谈并且需要类型对象
socket.getdefaulttimeout():返回新套接字对象的默认超时值(秒)(float)。值None表示新的套接字对象没有超时。首次导入套接字模块时,默认为None。
socket.setdefaulttimeout(timeout):为新的套接字对象设置默认的超时值(秒)(float)。首次导入套接字模块时,默认为None。请参阅 settimeout()可能的值和它们各自的含义。
socket.sethostname(name):将机器的主机名称设为名称。OSError如果你没有足够的权利,这将会提高 。
socket.if_nameindex():返回网络接口信息列表(index int,name string)元组。 OSError如果系统调用失败。
socket.if_nametoindex(if_name ):返回接口名称对应的网络接口索引号。 OSError如果没有给定名称的接口存在。
socket.if_indextoname(if_index ):返回接口索引号对应的网络接口名称。 OSError如果没有给定索引的接口存在。
3、socket链接
一般socket建立链接需要六个步骤,其中包括:socket.socket()创建socket对象、s.bind绑定地址到socket对象、s.listen监听地址端口、s.accept阻塞接受链接请求、s.send,s.recv方法处理通信数据、s.close关闭链接。
服务器创建套接字链接:
1)创建socket对象: socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
socket.socket(socket.AF_INET,socket.SOCK_STREAM)使用给定的地址族,套接字类型和协议号来创建一个新套接字.
>>> import socket #创建TCP socket: >>> sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建UDP socket: >>> sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
family为指定的地址族:
socket.AF_UNIX :只能够用于单一的Unix系统进程间通信
socket.AF_INET :服务器之间的网络通信(ipv4协议的TCP和UDP)ipv4,默认为这个
socket.AF_INET6 :服务器之间的网络通信ipv6
type为指定的套接字类型:
socket.SOCK_STREAM :面向连接的TCP,默认为这个
socket.SOCK_DGRAM :面向非连接的UDP
family和type参数是指定了一个协议,我们也可以使用proto第三个参数来直接指定使用的协议。我们也可以使用socket下的函数getprotobyname('tcp'),来代替IPPTOTO_XX变量.
>>> import socket >>> socket.getprotobyname('tcp') 6 >>> socket.IPPROTO_TCP 6
proto为指定的协议号,一般为0:
socket.IPPROTO_TCP :TCP传输协议
socket.IPPROTO_UDP :UDP传输协议
socket.IPPROTO_ICMP :ICMP链接
socket.IPPROTO_IP :IP链接
socket.IPPROTO_RAW :要构建IP头部和要发送的各种协议的头部和数据,包括ip头和协议和数据。
2)socket对象绑定地址及端口
地址必须是一个双元素的元组,包括(host,port)主机名或IP地址+端口号。如果端口号或地址错误将引发socke.error异常。
import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_TCP) HostPort = ('127.0.0.1',8898) s.bind(HostPort) #绑定地址端口
3)socket对象监听地址端口链接
socket.listen(backlog)
backlog指定了最多连接数,至少为1,接到连接请求后,这些请求必须排队等候连接,如果队列已满,则拒绝请求。
import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_TCP)
HostPort = ('127.0.0.1',8898) s.bind(HostPort) #绑定地址端口 s.listen(5) #监听最多5个连接请求
4)socket.accept对象阻塞等待接受链接
fd, addr = self._accept()
调用accept方法时,socket会进入‘waiting’阻塞状态,客户请求连接时,方法会建立连接并返回服务器。
accept方法会返回一个含有两个元素的元组,(fd,addr)。第一个元素是新的socket对象,服务器通过它与客户端通信。第二个元素是客户端的地址及端口信息。
import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) HostPort = ('127.0.0.1',8899) s.bind(HostPort) #绑定地址端口 s.listen(5) #监听最多5个连接请求 while True: print('server socket waiting...') obj,addr = s.accept() #阻塞等待链接 print('socket object:',obj) print('client info:',addr) #运行,链接输出信息 server socket waiting... socket object: <socket.socket fd=260, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8899), raddr=('127.0.0.1', 28237)> client info: ('127.0.0.1', 28237) server socket waiting...
5)处理阶段,服务器与客户端通过send和recv方法通信(传输数据)
调用新链接对象与客户端或者服务器通信:
socket.recv(buffersize) :接受客户端信或服务器数据,buffersize指定接收数据的大小,单位为字节。
socket.send(data) :发送信息给客户端或服务器,信息必须转换为字节才能发送。
import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) HostPort = ('127.0.0.1',8899) s.bind(HostPort) #绑定地址端口 s.listen(5) #监听最多5个连接请求 while True: print('server socket waiting...') obj,addr = s.accept() #阻塞等待链接,创建新链接对象(obj)和客户端地址(addr) while True: client_data = obj.recv(1024) #通过新链接对象接受数据 print(client_data) obj.send(client_data) #通过新链接对象发送数据
6)传输结束,关闭链接
socket.close() 关闭链接
客户端创建套接字链接:
1)s = socket.socket() 创建socket对象
2)s.connect('127.0.0.1','80') 绑定地址端口链接服务器
3)s.send(data) 发送数据到服务器
4)s.recv(1024) 接收服务器数据
5)s.close() 关闭链接
4、socket套接字对象方法
服务器段套接字:
s.bind() :绑定地址(host,port)到套接字,在AF_INET下,以元组(host,port)的形式表示地址。
s.listen() :开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept() :被动接受TCP客户端连接,(阻塞式)等待连接的到来
客户端套接字:
s.connect() :主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误
s.connect_ex() :connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数:
s.recv() :接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
s.send() :发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall() :完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
s.recvfrom() :接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
s.sendto() :发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
s.close() :关闭套接字
s.getpeername() :返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
s.getsockname() :返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value) :设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen]) :返回套接字选项的值。
s.settimeout(timeout) :设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout() :返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None
s.fileno() :返回套接字的文件描述符
s.setblocking(flag) :如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.makefile() :创建一个与该套接字相关连的文件
5、socket实例
1)简单的TCP socket会话
服务器代码:
import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) HostPort = ('127.0.0.1',8898) s.bind(HostPort) #绑定地址端口 s.listen(5) #监听最多5个连接请求 while True: print('server socket waiting...') c,addr = s.accept() #阻塞等待链接,创建套接字c链接和地址信息addr while True: try: client_date = c.recv(1024) #接收客户端数据 if str(client_date,'utf8') == 'quit': c.close() break except Exception: break c.send(client_date) #发送数据给客户端 print('clientINFO:',str(client_date, 'utf8')) #打印数据,默认接收数据为bytes,需转换成str
此处使用try捕捉异常,是在客户端断开链接时,服务器端会抛出异常,为了实现多连接排队链接,必须捕捉异常让程序正常运行,这种现象只在windows系统下存在;在linux下的表现形式又不同,在linux下不会抛出异常,而是正常接收客户端数据而不会退出,只需要判断客户端数据长度,正常退出急可解决问题。
客户端代码:
import socket hostport = ('127.0.0.1',8898) s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建TCP socket s.connect(hostport) #链接套接字 while True: user_input = input('>>>:').strip() s.send(bytes(user_input,'utf8')) #发送数据到套接字 if not len(user_input):continue if user_input == 'quit': s.close() break server_reply = s.recv(1024) #接收套接字数据 print(str(server_reply, 'utf8')) #打印输出
会话结果:
#首先运行服务器代码,然后运行客户端代码链接服务器 #client output: >>>:hello python hello python >>>:socket network programming socket network programming >>>: #server output: server socket waiting... clientINFO: hello python clientINFO: socket network programming
2)简单的UDP socket会话
服务器接收端:
import socket HostPort = ('127.0.0.1',7777) sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建UDP套接字 sock.bind(HostPort) #服务器端绑定端口 while True: data,addr = sock.recvfrom(1024) #接收端口数据 print(str(data,'utf8')) #打印数据
客户端发送端:
import socket HostPort = ('127.0.0.1',7777) sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True: user_input = input('>>>:') if user_input == 'quit':break sock.sendto(user_input.encode(),HostPort) #指定地址端口发送数据,数据必须encode sock.close()
3)socke实现简单版ssh
服务器代码:
import subprocess import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) HostPort = ('192.168.146.129',8821) s.bind(HostPort) s.listen(5) while True: print('server socket waiting...') c,addr = s.accept() #阻塞等待连接,并创建套接字和地址端口对象 while True: client_date = c.recv(1024) #接收数据 if not client_date:break #判断数据为空则跳出循环 if str(client_date,'utf8') == 'quit': #指定正常退出链接接口 c.close() break strule = client_date.decode() #解码数据 strule_out = subprocess.Popen(strule,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) #交互系统执行命名,并将输出和错误输出到管道 strule_out_read = strule_out.stdout.read() #读取管道数据 if not strule_out_read: #判断命令错误时,数据为错误输出信息 strule_out_read = strule_out.stderr.read() #在发送数据前,先发送数据长度到客户端并确认 coun =bytes("cmd_result_size|%s" %len(strule_out_read),'utf8') #print(coun) c.send(coun) #发送数据长度 client_ack = c.recv(50) #接收客户端确认信息 #print(str(client_ack,'utf8')) if str(client_ack,'utf8') == 'client ready to recv': #确认客户端后开始发送数据 c.send(strule_out_read) #print(str(strule_out_read,'utf8')) #print('clientINFO:',str(client_date, 'utf8'))
客户端代码:
import socket hostport = ('192.168.146.129',8821) s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建TCP socket s.connect(hostport) #链接套接字 while True: user_input = input('>>>:').strip() if not len(user_input):continue if user_input == 'quit': s.send(bytes('quit','utf8')) s.close() break else: s.send(bytes(user_input, 'utf8')) # 发送数据到套接字 server_ack = s.recv(100) #接收数据长度 ack_msg = str(server_ack.decode()).split('|') if ack_msg[0] == "cmd_result_size": #判断是否为数据长度包 ack_size = int(ack_msg[1]) #获取数据长度 s.send('client ready to recv'.encode()) #发送客户端确认信息 tool = 0 count_info = '' while tool < ack_size: #判断接收数据长度是否小于总数据长度,则循环接收数据 server_reply = s.recv(1024) #接收套接字数据 count_info +=str(server_reply.decode()) tool += len(server_reply) else: print(count_info) #正常接收完数据后打印
6、socketserver框架
该socketserver模块简化了编写网络服务器的任务;共有四个基本的具体服务器类:
class socketserver.TCPServer(server_address,RequestHandlerClass,bind_and_activate=True)
class DatagramRequestHandler(BaseRequestHandler): def setup(self): from io import BytesIO self.packet, self.socket = self.request self.rfile = BytesIO(self.packet) self.wfile = BytesIO() def finish(self): self.socket.sendto(self.wfile.getvalue(), self.client_address)DatagramRequestHandler
-
这些
BaseRequestHandler
子类重写setup()
和finish()
方法,提供self.rfile
和self.wfile
属性。该self.rfile
和self.wfile
属性可以被读取或写入,分别获得请求的数据或者数据返回给客户端。
3)socketserver实例
- socketserver.TCPServer示例:实现同步多并发链接,一个进程只能在处理完一个链接后才能处理第二个链接。
服务器端:
import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): ‘‘‘服务器处理请求类程序,每连接一次服务器实例化一次,并重写handle()方法来实现通信客户端’’’ def handle(self): print('new connection:',self.client_address) #获取客户端地址和端口 while True: self.data = self.request.recv(1024).strip() #request获取连接客户端的套接字,recv接收客户端数据 print('{}AddrAndPort:'.format(self.client_address)) print(self.data) self.request.sendall(self.data.upper()) #发送数据到客户端并转换大小写 if __name__ == "__main__": HOSTPORT = ('127.0.0.1',9988) #创建服务器,绑定地址端口和类 server = socketserver.TCPServer(HOSTPORT,MyTCPHandler) #运行服务器,相对于守护进程,直到按ctrl-c来结束程序 server.serve_forever()
- 使用预定义包装的类ThreadingTCPServer实现异步并发链接:
import socketserver class Mysocket(socketserver.StreamRequestHandler): #重写setup和finish方法 def handle(self): #处理链接 print('client:',self.client_address) while 1: data = self.request.recv(1024) self.request.send(data) print(data.decode()) if __name__ == '__main__': AddrPort = ('127.0.0.1',7899) server = socketserver.ThreadingTCPServer(AddrPort,Mysocket) #多线程创建实例链接 server.serve_forever() #循环接收链接
- windows系统下的一个bug:
import socketserver class Mysocket(socketserver.BaseRequestHandler): def handle(self): print('client:',self.client_address) while 1: data = self.request.recv(1024) self.request.send(data) print(data.decode()) if __name__ == '__main__': AddrPort = ('127.0.0.1',7899) #如果此处使用ForkingTCPServer类来创建多进程的实例化链接,在windows系统下会报错 server = socketserver.ForkingTCPServer(AddrPort,Mysocket) server.serve_forever() #链接后异常: File "Z:\Program Files\Python35\lib\socketserver.py", line 588, in process_request pid = os.fork() AttributeError: module 'os' has no attribute 'fork' #原因是: `os.fork` isn't available on Windows os.fork在windows上是不可用的。
- 简单版FTPServer:
#服务器代码: #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/1/12 11:02 # @Author : Py.qi # @File : SOCKSERVER_01.py # @Software: PyCharm from socketserver import ThreadingTCPServer,StreamRequestHandler import os,json host_path = os.getcwd() class MyHandler(StreamRequestHandler): def handle(self): print('clientaddr:',self.client_address) while True: try: data = self.request.recv(1024) except Exception: break if data.decode() == 'ls': #简单查看目录下的文件 lsdir = os.listdir(os.getcwd()) pi = json.dumps(lsdir) self.request.send(pi.encode()) if len(data.decode().split()) >= 2: action, filename = data.decode().split() print(action,filename) filename_cd = host_path + '\\' + filename if action == 'get': print(action) self.getfile(filename) print('getfiel..end') elif action == 'upt': #上传交给类方法处理 print(action) self.uptfile(filename) print('upt---end') elif action == 'cd': #简单cd命令 os.chdir(filename_cd) lsdir1 = os.listdir(filename_cd) p2 = json.dumps(lsdir1) self.request.send(p2.encode()) def getfile(self,filename): #文件下载 filename_path = host_path + '\\' + filename print(filename_path) with open(filename_path,'rb') as f: filedata = f.read() self.request.send(filedata) self.request.close() def uptfile(self,filename): #文件上传 self.request.send(bytes('ok','utf8')) filename_uptfile = host_path + '\\' + filename with open(filename_uptfile,'wb') as f1: uptdata = self.request.recv(1024) f1.write(uptdata) self.request.close() if __name__ == "__main__": hostprot = ('127.0.0.1',5556) server = ThreadingTCPServer(hostprot,MyHandler) print('connection to who...') server.serve_forever() #客户端代码: #!/usr/bin/env python #coding:utf8 #file:Administrator #time:20180104 import socket,os,json host_path = r'Z:\\' hostport = ('127.0.0.1',5556) s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建TCP socket #s.bind(hostport) s.connect(hostport) #链接套接字 while True: user_input = input('>>>:').strip() s.send(user_input.encode()) #发送数据到套接字 if not len(user_input):continue if user_input == 'quit': s.close() break server_reply = s.recv(1024) #接收套接字数据 if not server_reply:break if len(user_input.split()) > 1: if user_input.split()[0] == 'get': filename = user_input.split()[1] filename_path = host_path + filename with open(filename_path,'wb') as f: print('----') f.write(server_reply) if user_input.split()[0] == 'upt': #redy = s.recv(1024) print(server_reply) filename_u = user_input.split()[1] filename_u_path = host_path + filename_u with open(filename_u_path,'rb') as f1: upt_data = f1.read() print(upt_data) s.send(upt_data) elif len(user_input.split()) == 1: lsdata = server_reply.decode() print(lsdata)
- Python案例-网络编程-socket入门-server&client
- 【python】网络编程-SocketServer 实现客户端与服务器间非阻塞通信
- python 网络编程之socketserver模块
- python网络编程之socketserver 推荐
- python网络编程之TCP通信实例和socketserver框架使用例子
- Python基础学习(5)网络编程socket、文件上传、粘包问题、socketserver、IO多路复用、线程与进程、进程池、线程池、上下文管理、协程
- Python网络编程之SocketServer
- python网络编程:socketserver的基本使用
- python网络编程之TCP通信实例和socketserver框架使用例子
- [python&php 网络编程]socket缓冲区大小设置
- [python&php 网络编程]把socket改成阻塞或非阻塞模式
- Python 套接字socketserver网络编程
- python网络编程---服务端socketserver
- 网络编程__【TCP传输】(重点)【Socket & ServerSocket】
- [python&php 网络编程]完整的socket服务端客户端
- Python网络编程之socketserver实现多并发
- 网络编程之Socket&ServerSocket(一)
- Python编程-网络编程进阶(IO复用、Socketserver)
- 网络编程之SocketChannel & ServerSocketChannel & Selector