1:Python3中socket编程介绍
这里就不介绍网络编程的基础知识了,比如TCP/IP协议,OSI模型,TCP的三次握手等。下面直接介绍python中socket编程;
2:简单的点对点
只接受单个连接的服务端:
01_TcpServer.py:
<code>import
socketimport
time server = socket.socket() ip_port = ('127.0.0.1'
,8000
) server.bind(ip_port) server.listen(10
) print('启动服务:等待客户端的连接......'
) conn,addr = server.accept() print('客户端已连接:'
) print(conn) print(addr)while
True
: print('等待客户端的数据:'
) client_data = conn.recv(1024
) client_data = client_data.decode('utf-8'
) stru_t = time.localtime(float(client_data)) strTime = time.strftime('%Y-%m-%d %H:%M:%S'
,stru_t) print(f'接收来自
{addr}
的数据:{strTime}
' ) conn.send(str(time.time()).encode('utf-8'
)) conn.close() server.close()/<code>
01_TcpClient.py
<code># -*- coding: utf-8
-*- import socket importtime
client = socket.socket() ip_port = ('127.0.0.1'
,8000
) client.connect(ip_port)while
True: client.send(str(time
.time
()).encode('utf-8'
))'等待服务端的数据:'
) server_data = client.recv(1024
) server_data = server_data.decode('utf-8'
) stru_t =time
.localtime(float(server_data)) strTime =time
.strftime('%Y-%m-%d %H:%M:%S'
, stru_t)'来自服务端的数据:{strTime}'
)time
.sleep(10
) client.close
()/<code>
3:并发服务端
3.1:创建线程处理
对于每个客户端连接都创建一个线程来处理:
02_TcpServer.py:
<code>import
socketfrom
socketimport
SOL_SOCKET,SO_REUSEADDRimport
timeimport
threadingimport
tracebackdef
handle_client
(conn, addr)
: print(conn, addr)while
True
:try
: print(f'等待客户端
{addr}
的数据:') client_data = conn.recv(1024
) client_data = client_data.decode('utf-8'
) stru_t = time.localtime(float(client_data)) strTime = time.strftime('%Y-%m-%d %H:%M:%S'
, stru_t) print(f'接收来自
{addr}
的数据:{strTime}
') conn.send(str(time.time()).encode('utf-8'
))except
ConnectionResetError: val = traceback.format_exc() print(val)break
except
Exception: val = traceback.format_exc() print(val) print(f'关闭链接
{addr}
') conn.close()def
StartTcpServer
(ip,port)
: server = socket.socket() server.setsockopt(SOL_SOCKET, SO_REUSEADDR,1
) server.bind((ip,port)) server.listen(10
)while
True
:try
: print('等待客户端的连接......'
) conn, addr = server.accept() threading.Thread(target=handle_client, args=(conn, addr)).start()except
Exception: val = traceback.format_exc() print(val)break
server.close()if
__name__ =='__main__'
: StartTcpServer('127.0.0.1'
,8000
)/<code>
02_TcpClient.py:
<code>import
socketimport
timeimport
tracebackdef
StartTcpClient
(ip_port)
: client_list = [socket.socket()for
iin
range(3
)]for
clientin
client_list: print(client) client.connect(ip_port)while
True
:for
index,clientin
enumerate(client_list):try
: client.send(str(time.time()).encode('utf-8'
)) print(f'clirnt[
{index}
],等待服务端的数据:') server_data = client.recv(1024
) server_data = server_data.decode('utf-8'
) stru_t = time.localtime(float(server_data)) strTime = time.strftime('%Y-%m-%d %H:%M:%S'
, stru_t) print(f'来自服务端的数据:
{strTime}
') time.sleep(2
)except
Exception: val = traceback.format_exc() print(val) client.close() client_list.remove(client)if
__name__ =='__main__'
: StartTcpClient(('127.0.0.1'
,8000
))/<code>
3.2:socketserver模块处理
在上面使用每来一个连接,就创建一个线程的方式来处理,如果连接的数量过多,创建线程就会出现问题。
在Python中提供了socketserver模块,socketserver在内部使用IO多路复用以及多线程/进程机制,实现了并发处理多个客户端请求的socket服务端。
03_TcpServer.py
<code>import
socketserverimport
timeimport
tracebackdef
handle_client_data
(data,addr)
:try
: data = data.decode('utf-8'
) stru_t = time.localtime(int(data)) strTime = time.strftime('%Y-%m-%d %H:%M:%S'
, stru_t) print(f"来自
{addr}
的客户端向你发来信息:{data}
,转换之后:{strTime}
" )except
Exception:pass
class
TcpServer
(socketserver.BaseRequestHandler)
:""" 必须继承socketserver.BaseRequestHandler类 """
def
handle
(self)
:""" 必须实现这个方法! :return: """
conn = self.request conn.sendall(str(time.time()).encode('utf-8'
)) print(f"1111:
{self.client_address}
")while
True
:try
: data = conn.recv(1024
) handle_client_data(data,self.client_address) conn.sendall(str(int(time.time())).encode('utf-8'
))except
Exception: val = traceback.format_exc() print(val)break
print(f'退出客户端
{self.client_address}
的处理。')def
StartTcpServer
(ip,port)
: server = socketserver.ThreadingTCPServer((ip,port), TcpServer) print("启动socketserver服务器!"
) server.serve_forever()if
__name__ =='__main__'
: StartTcpServer('127.0.0.1'
,8000
)/<code>
03_TcpClient.py:
<code>import
socketimport
timeimport
tracebackdef
StartTcpClient
(ip_port,nums)
: client_list = [socket.socket()for
iin
range(nums)]for
clientin
client_list: print(client) client.connect(ip_port)while
True
:try
:for
index,clientin
enumerate(client_list):try
: client.sendall(str( int(time.time()) ).encode('utf-8'
)) print(f'clirnt[
{index}
],等待服务端的数据:') server_data = client.recv(1024
)try
: server_data = server_data.decode('utf-8'
) stru_t = time.localtime(float(server_data)) strTime = time.strftime('%Y-%m-%d %H:%M:%S'
, stru_t) print(f'来自服务端的数据:
{server_data}
转换之后:{strTime}
')except
Exception:pass
time.sleep(1
)except
(ConnectionResetError,ConnectionAbortedError): val = traceback.format_exc() print(val) client.close() client_list.remove(client)if
len(client_list)1
:raise
except
Exception: val = traceback.format_exc() print(val)except
Exception: val = traceback.format_exc() print(val) print('退出'
)break
def
test
()
: print(str(time.time() ),time.time(),int(time.time()))if
__name__ =='__main__'
: StartTcpClient(('127.0.0.1'
,8000
),1
) test()/<code>
3.3:使用select模块
Python中的select模块专注于I/O多路复用,提供了select poll epoll三个方法(其中后两个在Linux中可用,windows仅支持select),另外也提供了kqueue方法(freeBSD系统)
select方法:
进程指定内核监听哪些文件描述符(最多监听1024个fd)的哪些事件,当没有文件描述符事件发生时,进程被阻塞;当一个或者多个文件描述符事件发生时,进程被唤醒。
04_TcpServer.py:
<code>import
socketimport
selectfrom
socketimport
SOL_SOCKET,SO_REUSEADDRimport
timeimport
tracebackdef
handle_client_data
(data,addr)
:try
: data = data.decode('utf-8'
) stru_t = time.localtime(int(data)) strTime = time.strftime('%Y-%m-%d %H:%M:%S'
, stru_t) print(f"来自
{addr.getpeername()}
的客户端向你发来信息:{data}
,转换之后:{strTime}
")except
Exception:pass
def
StartTcpServer
(ip,port)
: server = socket.socket() server.setsockopt(SOL_SOCKET, SO_REUSEADDR,1
) server.bind((ip,port)) server.listen(10
) read_fd_list = [server,]while
True
:try
: r_list, w_list, error_list = select.select(read_fd_list, [], [],1
) stru_t = time.localtime(time.time()) strTime = time.strftime('%Y-%m-%d %H:%M:%S'
, stru_t) print(f'select之后:len(r_list)=
{len(r_list)}
,时间:{strTime}
')for
fdin
r_list:if
fd == server: conn, addr = fd.accept() print(addr) read_fd_list.append(conn) data = conn.recv(1024
) print(f'接收数据:msg=
{data}
') handle_client_data(data, conn) conn.sendall(str(time.time()).encode('utf-8'
))else
:try
: data = fd.recv(1024
) handle_client_data(data,fd) fd.sendall(str(int(time.time())).encode('utf-8'
))except
(ConnectionResetError,ConnectionAbortedError,ConnectionRefusedError): val = traceback.format_exc() print(val) fd.close() read_fd_list.remove(fd)except
Exception: val = traceback.format_exc() print(val)break
server.close()if
__name__ =='__main__'
: StartTcpServer('127.0.0.1'
,8000
)/<code>
04_TcpClient.py:
<code>import
socketimport
timeimport
tracebackdef
StartTcpClient
(ip_port,nums)
: client_list = [socket.socket()for
iin
range(nums)]for
clientin
client_list: print(client) client.connect(ip_port)while
True
:try
:for
index,clientin
enumerate(client_list):try
: client.sendall(str( int(time.time()) ).encode('utf-8'
)) print(f'clirnt[
{index}
],等待服务端的数据:') server_data = client.recv(1024
) server_data = server_data.decode('utf-8'
) stru_t = time.localtime(float(server_data)) strTime = time.strftime('%Y-%m-%d %H:%M:%S'
, stru_t) print(f'来自服务端的数据:
{server_data}
转换之后:{strTime}
')except
(ConnectionResetError, ConnectionAbortedError, ConnectionRefusedError): val = traceback.format_exc() print(val) client.close() client_list.remove(client)if
len(client_list)1
:raise
except
Exception: val = traceback.format_exc() print(val)except
Exception: val = traceback.format_exc() print(val) print('退出'
)break
if
__name__ =='__main__'
: StartTcpClient(('127.0.0.1'
,8000
),1
) /<code>
4:socket实现web服务器
4.1:简单web服务器
在浏览器中访问
http://127.0.0.1:8000/home 等
<code>import
socketdef
StartWebServer
(ip,port)
: sk = socket.socket() sk.bind((ip,port)) sk.listen()while
True
: conn, addr = sk.accept() data = conn.recv(8096
) print(data) conn.send(b'HTTP/1.1 200 OK\r\n\r\n'
)try
: data = data.decode('utf-8'
) url = data.split()[1
] ret_data = urlif
url =="/test"
: ret_data ="test"
elif
url =="/home"
: ret_data ="home"
elif
url =="/index"
: ret_data ="index"
else
: ret_data ="404"
conn.send(f'
{ret_data}
'.encode('utf-8'
))except
Exception:pass
conn.close()if
__name__ =='__main__'
: StartWebServer('127.0.0.1'
,8000
)/<code>
4.2:简单web服务器:函数版本
<code>import
socketimport
timedef
test
(url)
: ret =f'test:
{url}
'return
ret.encode('utf-8'
)def
index
(url)
: ret =f'index:
{url}
'return
ret.encode('utf-8'
)def
home
(url)
: ret =f'home:
{url}
'return
ret.encode('utf-8'
)def
gettime
(url)
: now = time.strftime('%Y-%m-%d %H:%M:%S'
) ret =f'time:
{url}
{now}
'return
ret.encode('utf-8'
) url_map = {'/test'
:test,'/index'
:index,'/home'
:home,'/time'
:gettime}def
handle_client_data
(data)
: data = data.decode('utf-8'
) url = data.split()[1
] ret_data = urlif
urlin
url_map: func = url_map[url] ret_data = func(url)else
: ret_data =b"404"
return
ret_datadef
StartWebServer
(ip,port)
: sk = socket.socket() sk.bind((ip,port)) sk.listen()while
True
: conn, addr = sk.accept() data = conn.recv(8096
) print(data) conn.send(b'HTTP/1.1 200 OK\r\n\r\n'
)try
: ret_data = handle_client_data(data) conn.send(ret_data)except
Exception:pass
conn.close()if
__name__ =='__main__'
: StartWebServer('127.0.0.1'
,8000
)/<code>
在浏览器中访问:
http://127.0.0.1:8000/time
4.3:简单web服务器:返回动态页面
<code>import
socketimport
timedef
test
(url)
: ret =f'test:
{url}
'return
ret.encode('utf-8'
)def
index
(url)
:with
open('index.html'
,'rb'
)as
f: ret = f.read()return
retdef
home
(url)
: ret =f'home:
{url}
'return
ret.encode('utf-8'
)def
gettime
(url)
: now = time.strftime('%Y-%m-%d %H:%M:%S'
)with
open('time.html'
,'r'
, encoding='utf-8'
)as
f: data = f.read() data = data.replace('##time##'
, now)return
data.encode('utf-8'
) url_map = {'/test'
:test,'/index'
:index,'/home'
:home,'/time'
:gettime}def
handle_client_data
(data)
: data = data.decode('utf-8'
) url = data.split()[1
] ret_data = urlif
urlin
url_map: func = url_map[url] ret_data = func(url)else
: ret_data =b"404"
return
ret_datadef
StartWebServer
(ip,port)
: sk = socket.socket() sk.bind((ip,port)) sk.listen()while
True
: conn, addr = sk.accept() data = conn.recv(8096
) print(data) conn.send(b'HTTP/1.1 200 OK\r\n\r\n'
)try
: ret_data = handle_client_data(data) conn.send(ret_data)except
Exception:pass
conn.close()if
__name__ =='__main__'
: StartWebServer('127.0.0.1'
,8000
)/<code>
index.html
<code> ><
html
lang
="en"
><
head
><
meta
charset
="UTF-8"
><
title
>indextitle
>head
><
body
><
div
><
h1
>indexh1
><
h2
>indexh2
><
h3
>indexh3
><
h4
>indexh4
>div
>body
>html
>/<code>
time.html
<code> ><
html
lang
="en"
><
head
><
meta
charset
="UTF-8"
><
title
> timetitle
>head
><
body
><
h1
>当前时间是: ##time##h1
>body
>html
>/<code>
4.4:并发web服务器
使用socketserver实现:
<code>import
socketserverimport
timeimport
tracebackdef
test
(url)
: ret =f'test:
{url}
'return
ret.encode('utf-8'
)def
index
(url)
:with
open('index.html'
,'rb'
)as
f: ret = f.read()return
retdef
home
(url)
: ret =f'home:
{url}
'return
ret.encode('utf-8'
)def
gettime
(url)
: now = time.strftime('%Y-%m-%d %H:%M:%S'
)with
open('time.html'
,'r'
, encoding='utf-8'
)as
f: data = f.read() data = data.replace('##time##'
, now)return
data.encode('utf-8'
) url_map = {'/test'
:test,'/index'
:index,'/home'
:home,'/time'
:gettime}def
handle_client_data
(data)
: data = data.decode('utf-8'
) url = data.split()[1
] ret_data = urlif
urlin
url_map: func = url_map[url] ret_data = func(url)else
: ret_data =b"404"
return
ret_dataclass
TcpServer
(socketserver.BaseRequestHandler)
:""" 必须继承socketserver.BaseRequestHandler类 """
def
handle
(self)
:""" 必须实现这个方法! :return: """
conn = self.requestwhile
True
:try
: data = conn.recv(8096
) print(data) conn.send(b'HTTP/1.1 200 OK\r\n\r\n'
)try
: ret_data = handle_client_data(data) conn.sendall(ret_data)except
Exception:pass
break
except
Exception: val = traceback.format_exc() print(val)break
print(f'退出客户端
{self.client_address}
的处理。')def
StartTcpServer
(ip,port)
: server = socketserver.ThreadingTCPServer((ip,port), TcpServer) print("启动socketserver服务器!"
) server.serve_forever()if
__name__ =='__main__'
: StartTcpServer('127.0.0.1'
,8000
)/<code>