技术点
服务器进程绑定一个端口,利用主线程监听来自其他客户端的连接。如果有客户端建立链接,开启子线程处理事务。
ThreadLocal可以为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。不再需要建立局部变量和全局变量为每个线程服务。
完整的代码放在文末

测试用客户端
使用Windows的telnet服务,win+R开启DOS命令窗,

telnet ip port

链接服务器,win10开启telnet服务可以参考链接: →telnet.

  1. 基础服务器
#库
import socket
import threading

整个socket服务器封装成类threadSocket
在构造初始化中主要就是工作方式的设置和ip、端口的绑定
socket.SOL_SOCKET
在套接字级别上设置工作方式时,需要选择SOL_SOCKET
socket.SO_REUSEADDR
当socket关闭后,本地端用于该socket的端口号立刻就可以被重用。这个可以减少TIME_WAIT的存在

#构造函数
def __init__(self,host,port):
        self.host=host
        self.port=port
        #基于TCP的数据流,IPV4
        self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#不需要timewait等待施放端口
        self.sock.bind((self.host,self.port))

服务器类需要一个负责监听的成员函数listen

def listen(self):
        self.sock.listen(5)
        #监听
        while True:
            client,addr=self.sock.accept()
            client.settimeout(60)#连接时限
            threading.Thread(target=self.handleClientRequest,args=(client,addr)).start()

处理链接的成员函数handleClientRequest

def handleClientRequest(self,session,addr):
        session.send('Welcome to my world.\r\n')
        while True:
            #try:
            self.data+=session.recv(1024)
            if '\r\n' in self.data:
                line=self.data
                self.data=''
                session.send(line)

这样就完成了简单的请求与应答,self.data是类内的全局字符串,主要是按照客户端的回车作为一次接收的数据

python ThreadPoolExecutor cpu 内存无法释放 threadlocal python_服务器

  1. 正则表达式

正则表达式可以用来匹配字符串,需要加入头文件import re
服务器需要对客户端的某些命令进行响应,比如GET获取信息,POST添加信息使用正则表达式的方法:
GET信息可以利用 ^GET\r\n$ 进行匹配

^表示行的开头,$表示行的结束

POST的命令格式化是,POST/x=do somthing,可以用POST/.=. 来匹配

.表示任意,加上r可以忽略转义问题

re.match(r'POST/*.=*.',line)

更改后handleClientRequest变为

def handleClientRequest(self,session,addr):
        session.send('Welcome to my world.\r\n')
        while True:
            #try:
            self.data+=session.recv(1024)
            if  self.data[-2:] == '\r\n':
                line=self.data
                self.data=''
                if re.match('^GET\r\n$',line):
                    response=str(self.todo_list)
                    line=response+'\r\n'
                elif re.match(r'POST/.=.',line) :
                    key,command=line.split('/')
                    id,value=command.split('=')
                    self.todo_list[id]=value[:-2]
                    line='post ok\r\n'
                else:
                    pass
                session.send(line)

可以匹配GETPOST命令

打开telnet测试结果,

python ThreadPoolExecutor cpu 内存无法释放 threadlocal python_客户端_02

  1. ThreadLocal
    待更新
#源码
# -*- coding: utf-8 -*-
#
#主线程等待客户端的连接,一旦连接成功就启动一个新的子线程,并且把读写相关的操作都扔给子线程处理
#
import socket
import threading
import re

class threadSocket(object):

    # 服务
    todo_list={
        'task_01':'see someone',
        'task_02':'read book',
        'task_03':'play basketball',
        }
    data=''

    def __init__(self,host,port):
        self.host=host
        self.port=port
        self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#不需要timewait等待施放端口
        self.sock.bind((self.host,self.port))

    def listen(self):
        self.sock.listen(5)
        #监听
        while True:
            client,addr=self.sock.accept()
            client.settimeout(60)#连接时限
            threading.Thread(target=self.handleClientRequest,args=(client,addr)).start()

    def handleClientRequest(self,session,addr):
        session.send('Welcome to my world.\r\n')
        while True:
            #try:
            self.data+=session.recv(1024)
            if  self.data[-2:] == '\r\n':
                line=self.data
                self.data=''
                if re.match('^GET\r\n$',line):
                    response=str(self.todo_list)
                    line=response+'\r\n'
                elif re.match(r'POST/*.=*.',line) :
                    key,command=line.split('/')
                    id,value=command.split('=')
                    self.todo_list[id]=value[:-2]
                    line='post ok\r\n'
                else:
                    pass
                session.send(line)                                                             

if __name__=='__main__':
    
    server=threadSocket('',9000)
    server.listen()