python socket 和 WebSocket 简介

推荐先看:

TCP/IP、HTTP、Socket、WebSocket-CSDN博客

用了这么久,WebSocket和Socket的区别,你真的知道吗?-CSDN博客 


python socket和websocket对待client并发的处理:

用socketserver.ThreadingTCPServer处理socket的多并发,与 from ws4py.client.threadedclient import WebSocketClient相比:

(1)socketserver.ThreadingTCPServer 在server端启动后,会一直轮询查selector中是否有可执行的socket,当监听到client的连接时,就会把client放到selector队列中,如果selector中存在client可执行了,就会在server端为每个可执行的client开启一个线程处理读写任务,任务执行完后就关闭socket client。

        这个socket server是否端口复用,执行任务的线程是否守护线程,都可自定义。默认是端口不可复用,线程是非守护线程。

(2)ws4py下的WebSocketClient 是直接由客户端自己控制,当client连接上server后,client端会直接开启一个守护线程处理读写任务。守护线程不会主动关闭。

1、socket

1.1 简介

博文推荐:Socket的长连接和短连接

说明:socket本身无长短,因使用方式的不同被分为长连接、短连接。

  • 长连接:一旦建立了socket连接,就不会轻易断开,除非客户端或服务端主动发起关闭,否则连接一直开着。这种情况下,只要一方有消息发来,不管发起几次,另一方都能立马接受。也就是可以一次建立,多次交流。
  • 短连接:一旦建立了socket连接,完成一来一回的信息交流后,就必须关闭。如果还要没有更多的消息要传递,那只能再次重建连接。每次连接也只能是一轮消息交流,即一方只有1次发送消息的机会,另一方只有1次接受此消息的机会。也就是一次建立,一次交流。

1.2 socket案例

(1)socketserver_server.py 
# Autor cf
#!/usr/bin/env Python
# coding=utf-8

import socketserver
import traceback
import time


class MySockServer(socketserver.BaseRequestHandler):  # 定义一个类

    # 在handle()之前被调用,主要的作用就是执行处理请求之前的初始化相关的各种工作。默认不会做任何事。
    def setup(self): #
        print("开始初始化工作~~~")

    # handle(self)处理与请求相关的工作。默认也不会做任何事。
    def handle(self):
        print ('Got a new connection from client:', self.client_address)
        print ('Got a new connection to server:', self.server)
        while True:
            data = self.request.recv(1024).decode('UTF-8', 'ignore').strip()  # 需要通过self的方法调用数据接收函数
            if not data:
                break
            print('recv:', data)
            self.request.send(data.upper().encode("utf8"))  # 需要通过self的方法调用数据发送函数

    # 在handle()方法之后会被调用,他的作用就是执行当处理完请求后的清理工作,默认不会做任何事
    def finish(self):
        print('准备睡觉啦~~~~~')
        time.sleep(5)
        print('睡醒了~~~~~')
        print("任务处理完成,开始收尾工作~~~")



HOST = '192.168.10.4'             #定义侦听本地地址口(多个IP地址情况下)
PORT = 12345          #Server端开放的服务端
# ForkingMixIn利用多进程(分叉)实现异步。
# ThreadingMixIn利用多线程实现异步。
# ThreadingTCPServer继承了ThreadingMixIn和TCPServer
# 调用SocketServer模块的多线程并发函数,这里的多线程是每次来一个请求都new thread去处理的
s = None
try:
    # allow_reuse_address=True允许服务器重用地址,含义:即使端口已被占用,仍不影响该端口的使用
    # socketserver.ThreadingTCPServer.allow_reuse_address = True
    # 这里构造器传递的参数是TCPServer初始化需要的信息
    s = socketserver.ThreadingTCPServer((HOST, PORT), MySockServer)
    s.serve_forever()     #持续接受客户端的连接,会把连接放到selector
except Exception as e:
    traceback.print_exc()
finally:
    print('server finally...........')
    s.shutdown()
(2)client.py 短链接
  • socket 短连接, 需要满足:socket client 和 server 完成一来一回的 “一轮次” 消息交流后,就必须关闭。
# Autor cf
#!/usr/bin/env Python
# coding=utf-8

import socket
import time
import traceback

HOST, PORT = '192.168.10.4' , 12345
data = "the weather is warm, let's go out for shopping"

m = 0

while(True):
    sock = None
    print('start time = ', time.time())
    try:
        # Create a socket (SOCK_STREAM means a TCP socket)
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # Connect to server and send data
        sock.connect((HOST, PORT))
        m = m +1
        sock.sendall(bytes(str(m) + data, "utf-8"))

        # Receive data from the server and shut down
        received = str(sock.recv(1024), "utf-8")
        print("Sent:     {}".format(data))
        print("Received: {}".format(received))
    except Exception as e:
        print('end time = ', time.time())
        print(traceback.print_exc())
        break
    finally:
        print('client finally close')
        sock.close()
(3)client2.py 长连接
  • socket 长连接, 需要满足:socket client 和 server 一直保持连接状态;
  • 由于连接一直开启,所以每次通信,根据指定的消息结束符(如EOF、exit等)来判断一次消息是否发送结束;
  • 虽然长连接的状态是指“一直保持连接”,但也不是说永远永远都不能端。实际应用中,socket client的关闭时刻可以根据不同的应用场景来设计。比如:只在异常发生时 或 客户端长时间无动态 或 客户端主动发起关闭(如关闭聊天窗口)等。
# Autor cf
#!/usr/bin/env Python
# coding=utf-8


'''
socket 长连接, 需要满足以下条件:
(1) socket client 在while(True)模式下一直发消息;
(2) 每次通信,根据指定的消息结束符(如EOF、exit等)来判断一次消息是否发送结束;
(3) socket client的关闭可以根据应用场景设计不同的关闭时刻。比如:只在异常发生时 或 客户端长时间无动态 或 客户端主动发起关闭(如关闭聊天窗口)等。

'''
import socket
import time
import traceback

data = "the weather is warm, let's go out for shopping"
m = 0

HOST, PORT = '192.168.10.4' , 12345
# Create a socket (SOCK_STREAM means a TCP socket)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect to server and send data
sock.connect((HOST, PORT))

print('start time = ', time.time())
try:
    while (True):
        m = m +1
        sock.sendall(bytes(str(m) + data, "utf-8"))
        # Receive data from the server and shut down
        received = str(sock.recv(1024), "utf-8")
        print("Sent:     {}".format(data))
        print("Received: {}".format(received))
except Exception as e:
    print('end time = ', time.time())
    sock.close()  # 测试sock长连接
    raise Exception(e, 'client通信失败')

 2、WebSocket案例

(1) webserver.py

# Autor cf
#!/usr/bin/env Python
# coding=utf-8


import asyncio
import websockets

# 检测客户端权限,用户名密码通过才能退出循环
async def check_permit(websocket):
    while True:
        recv_str = await websocket.recv()
        cred_dict = recv_str.split(":")
        if cred_dict[0] == "admin" and cred_dict[1] == "123456":
            response_str = "congratulation, you have connect with server\r\nnow, you can do something else"
            await websocket.send(response_str)
            return True
        else:
            response_str = "sorry, the username or password is wrong, please submit again"
            await websocket.send(response_str)

# 接收客户端消息并处理,这里只是简单把客户端发来的返回回去
async def recv_msg(websocket):
    while True:
        recv_text = await websocket.recv()
        response_text = f"your submit context: {recv_text}"
        await websocket.send(response_text)

# 服务器端主逻辑
# websocket和path是该函数被回调时自动传过来的,不需要自己传
async def main_logic(websocket, path):
    await check_permit(websocket)

    await recv_msg(websocket)

# 把ip换成自己本地的ip
start_server = websockets.serve(main_logic, '192.168.10.4', 12345)
print('start_server是:',start_server)

# 如果要给被回调的main_logic传递自定义参数,可使用以下形式
# 一、修改回调形式
# import functools
# start_server = websockets.serve(functools.partial(main_logic, other_param="test_value"), '10.10.6.91', 5678)
# 修改被回调函数定义,增加相应参数
# async def main_logic(websocket, path, other_param)

loop = asyncio.get_event_loop()
loop.run_until_complete(start_server)
loop.run_forever()

(2) webclient.py

1、继承关系(子->父):CG_Client -> WebSocketClient -> WebSocketBaseClient -> WebSocket

2、WebSocketClient在初始化__init__()方法中触发了WebSocketBaseClient的初始化; 
WebSocketBaseClient在初始化__init__()方法中又触发了WebSocket的初始化。

3、这里重写的opend、closed、received_message方法均来自WebSocket。

4、用ws4py中的WebSocketClient启动socket连接,比websockets.connect直接启动要稳定些。

# Autor cf
#!/usr/bin/env Python
# coding=utf-8

from ws4py.client.threadedclient import WebSocketClient

class CG_Client(WebSocketClient):

    def opened(self):
        cred_text = input("please enter your username and password: ")
        self.send(cred_text)


    def closed(self, code, reason=None):
        print("Closed down:", code, reason)



    def received_message(self, resp):
        print('recieved:', resp)
        # ws.close() # 这里不执行close,client会一直运行


if __name__ == '__main__':
    ws = None
    try:
        # (1)自定义WebSocketClient的子类,ws是自定义子类的一个实例,每个实例底层会另起一个守护线程,守护线程默认最大空闲存活时间默认0.1*60=6s。
        ws = CG_Client('ws://192.168.10.4:12345')
        # (2)connect()来自WebSocketBaseClient,该方法默认会先启动一个端口可复用的TCPSocket,再调用self.handshake_ok()
        # 注意:
        # 这里的self指的是最外层的调用对象,也就是ws, 而不是WebSocketBaseClient对象!由于ws最先继承WebSocketClient,所以
        # 这里触发的将是最外层父类WebSocketClient的handshake_ok(),而不是WebSocketBaseClient的handshake_ok();
        # (3)前者在handshake_ok()中触发了守护线程的启动,调用守护线程的run();
        # 这里守护线程调用的run()是WebSocket中的run();
        # (4)在WebSocket的run()中又调用了self.opend(), 这里就回到了ws.opend()了。
        ws.connect()
        # run_forever会把守护线程join到主线程下,所以主线程要等待守护线程结束才结束。
        ws.run_forever()
    except KeyboardInterrupt:
        ws.close()

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值