单线程-非阻塞-长链接

1. 基础框架

def main():
    pass

if __name__ == '__main__':
    main()
复制代码

2. 创建服务器socket

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcp_server_socket.setopt()

local_addr = ("",7890)
tcp_server_socket.bind(local_addr)
tcp_server_socket.listen(128)
复制代码

socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 在tcp的四次挥手中,如果哪一方先调用了close,哪一方就会等待2*MSL(最大报文寿命),在此期间,占用的端口资源不会释放,如果是服务器主动close,会导致重启后报错“端口被占用”,这句话的作用,设置socket当close立即释放资源

3. 接受请求,处理请求

设置监听套接字为非阻塞

tcp_server_socket.setblocking(False) 
复制代码

此时当监听套接字调用accept的时候有两种情况: 1. 正常接收端客户端链接,返回一个元组(clien_socket,client_addr) 2. 报错

client_socket_list = []
while True:
    try:
        client_socket,client_addr = tcp_server_socket.accept()
    except:
        pass
    else: 
        client_socket_list.append(client_socket)
复制代码

由于服务器不止服务一个客户端,所以链接进行的socket可以保存在一个列表中,不停的轮询

    for client_socket in client_socket_list:
        recv_data = client_socket.recv(1024)
复制代码

此时,如果clent_socket是阻塞的方式,会导致程序卡在这里,后面的客户端无法进行服务,所以,必须把client_socket也设置成非阻塞,此时如果调用clent_socket.recv(1024)会产生一下两种情况:

1. 正常收到数据(有真实数据,空数据(客户端调用了close的情况))
2. 报错
复制代码
    else: 
        client_socket.setblocking(False)
        client_socket_list.append(client_socket)

    for client_socket in client_socket_list:
        try:
            recv_data = client_socket.recv(1024)
        except:
            pass
        else:
            # 收到真实数据
            if recv_data:
                # 为客户端服务
            else: # 远程的客户端已经关闭了
                cliet_socket.close()
                client_socket_list.remove(cliet_socket)
复制代码

在上述代码中,如果recv_data为空,表示客户端主动断开了链接,导致recv收到一个空数据包,此时对于服务器而言,没必要在保留和这个客户端通信的socket,直接关闭,并且从列表中移除

4. 为客户端服务

由于客户端是浏览器,和服务器交流采用的是http协议 http协议包含两大部分:

  1. 请求部分
    1. 请求行 GET /index.html HTTP/1.1 请求方法,资源路径,http协议版本
    2. 请求头
    3. 请求体
  2. 响应部分
    1. 响应行 HTTP/1.1 200 OK
    2. 响应头
    3. 响应体

响应体是真正浏览器需要的数据,而在响应头中,有该数据的相关描述,比如Content-Type: text/html; charset=utf-8,表示响应体是html文本数据,并且采用utf-8编码,比如Content-Length: 128,表示响应体中数据的长度,如果服务器希望做到长链接,必须添加这个字段,让浏览器能够清楚验证啥时候数据请求完毕

def server_client(clent_socket,recv_data):
    recv_data = recv_data.decode("utf-8)
    # 由于recv_data是HTTP协议的请求字符串,可以按行进行分割
    request_lines = recv_data.splitlines()
    # 在请求行中获取请求的资源路径
    file_name = request_lines[0].split(" ")[1]
复制代码

当获得资源路径后,就可以在服务器中加载资源,由于客户端的不确定性,导致资源路径可能是错的,所以在读取资源的时,需要捕获异常

    try:
        with open(file_name,"rb") as f:
            response_body = f.read()
        # 一个完整的响应 = 响应行+响应头+'\r\n'+响应体
        response_header = "HTTP/1.1 200 OK\r\n"
        
    except:
        response_body = b'file not found'
        response_header = "HTTP/1.1 404 NOT FOUND\r\n"
       
    # 为了实现长链接,需要添加Content-Length
    response_header += "Content-Length: %d\r\n"%len(response_body)
    response = response_header.encode() + "\r\n"+ response_body
    client_socket.send(response)
复制代码

此时服务器不能调用client_socket.close()方法,否则就是短链接,效率太低

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值