异步asyncore实现多人聊天室以及struct的基本创建与使用

struct

udp_struct客户端

udp_socket是面向无连接的不可靠的通信,所以建立通信比较简单

  1. 建立连接 socket
  2. 绑定连接 bind
  3. 发送信息 sendto(str,(ip,port))
  4. 关闭连接 close
# 1创建一个套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2绑定一个本地信息
localaddr = ("", 7788)
udp_socket.bind(localaddr)
while True:
  # 3发送数据
   str=input("please input:")
   udp_socket.sendto(str.encode("utf-8"),("10.11.162.208",8080))
   if str=='exit':
       break
# 4关闭一个套接字
udp_socket.close()

udp_socket服务端

  1. 建立一个套接字 socket
  2. 绑定本地消息 bind
  3. 接受数据 recvfrom(1024)
  4. 关闭连接 close
# 1创建一个套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2绑定一个本地信息
localaddr = ("", 7788)
udp_socket.bind(localaddr)
while True:
  # 3接收数据
   rece_data = udp_socket.recvfrom(1024)
# rece_data这个变量中存储的是一个元组(接收到的数据,(发送方的ip,port))
   recv_msg = rece_data[0]  # 储存接收到的数据
   send_addr = rece_data[1]  # 储存发送方的地址信息
# 4打印数据
#    print(rece_data)
   print(("%s:%s" % (str(send_addr),  recv_msg.decode("gbk"))))
# 5关闭一个套接字
udp_socket.close()

tcp_socket是面向连接的可靠的一对一的通信,所以建立相对步骤多一些

tcp_socket客户端

  1. 创建一个套接字 socket
  2. 绑定一个服务器 bind
  3. 连接一个服务器 connect()
  4. 发送数据 send
  5. 关闭连接 close
#     1.创建一个套接字
    tcp_stock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#     2.连接一个服务器
#      2.1绑定一个服务器
    f_id=input("please input id:")
    f_port=int(input("please input port:"))
    f_addr=(f_id,f_port)
    #3连接一个服务器
    tcp_stock.connect(f_addr)
#     4.发送数据
    msg=input("please input msg;")
    tcp_stock.send(msg.encode("gbk"))
#     5.关闭套接字
    tcp_stock.close()

tcp_socket服务端

tcp_server创建过程类似买了手机打电话的过程
1.创建一个套接字(买个手机)
2.bind绑定ip和port和信息(插入手机卡)
3.让默认的套接字右主动变为被动listen(让手机设置为正常的
响铃接收模式)
4.等待客户端的连接 accept(等待别人的来电)
5.接收客户端的消息 recv
6.关闭连接       close
#   1.创建一个套接字(买个手机)
    tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#   2.bind绑定ip和port和信息(插入手机卡)
    local_addr=("",7788)
    tcp_server_socket.bind(local_addr)
#   3.让默认的套接字右主动变为被动listen(让手机设置为正常的
    tcp_server_socket.listen(128)
#   4.等待客户端的连接 accept(等待别人的来电)
    while True:
        print('---等待新客户端连接')
        new_client_socket,client_addr=tcp_server_socket.accept()
        print('---2----\n'
          ,client_addr,"客户端连接成功,等待客户端发送消息")
#   接收客户端发送来得请求
        while True:
            recv_data=new_client_socket.recv(1024)
            print("接收到了客户端的消息:%s" %recv_data.decode("gbk"))
# 回送一些数据给客户端
            new_client_socket.send("回送消息:问题正在解决---ok--".encode('gbk'))
#     关闭套接字
            if recv_data!='exit':
                new_client_socket.send("回送消息:问题正在解决---ok--".encode('gbk'))
            else:
                break
        print("当前客户已结束服务")
        new_client_socket.close()
    tcp_server_socket.close()
socket多线程实现多用户连接通信

基本思路

tcp_socket客户端和服务端的创建和之前的一样,只不过在服务端接收recv时,使用了多线程来调用接收和回送方法,避免同一线程会覆盖new_client_socket

import socket
import threading


def handle_socket(client, addr):
    while True:
        data = client.recv(1024)
        print("接收到客户端%s的消息:%s" % (addr[1], data.decode("gbk")))
        to_client = input("please input:")
        client.send(to_client.encode())
        if data == b'exit':
            break
    client.close()
def main():
    # 1.创建一个套接字(买个手机)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #   2.bind绑定ip和port和信息(插入手机卡)
    local_addr = ("", 7788)
    tcp_server_socket.bind(local_addr)
    #   3.让默认的套接字右主动变为被动listen(让手机设置为正常的
    tcp_server_socket.listen(128)
    #   4.等待客户端的连接 accept(等待别人的来电)
    while True:
        new_client_socket, client_addr = tcp_server_socket.accept()
        print(client_addr, "客户端连接成功")
        # 多线程实现多用户登录,不覆盖new_client_socket
        client_thread = threading.Thread(target=handle_socket,args=(new_client_socket, client_addr))
        client_thread.start()
if __name__ == '__main__':
    main()
使用异步asyncore实现多客户端的聊天室

聊天室服务器各个类的说明

聊天室服务端
ChatServer:类似socket_server,负责接收客户端的连接请求和创建socket对象
建好的socket对象要传给ChatSession对象进行保存
ChatSession:保存socket对象,负责与客户端进行实际通信 ,继承async_chat这个父类,主要的方法:
collect_incoming_data用于数据的存储,found_terminator设置聊天的数据传输规则,以换行符标识
每一条消息,handle_close退出聊天
CommandHandler:用于自定义协议,自定义方法的预设格式,例如方法都是do_开头
然后在代码中通过反射的方法直接调用
Room:一:维护存在的所有的用户的session列表,以及广播发送消息处理
二:维护当前用户的聊天状态,比如:LoginRoom、login_Room.ChatRoom
import asyncore, asynchat


class EndSession(Exception):
    pass


class ChatServer(asyncore.dispatcher):

    def __init__(self, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket()
        self.set_reuse_addr()
        self.bind(('localhost', port))
        self.listen(5)
        self.users = {}  # 记录当前用户的一个字典

        self.main_room = ChatRoom(self)

    def handle_accepted(self, sock, addr):
        print('当前连接', sock, addr)
        ChatSession(self, sock)


class ChatSession(asynchat.async_chat):
    '''
    负责和客户端进行通信
    '''

    def __init__(self, server, sock):
        asynchat.async_chat.__init__(self, sock)
        self.server = server
        self.set_terminator(b'\n')  # 设置聊天数据分隔协议,以换行符作为数据结束的表示
        self.data = []
        self.name = None  # 保存用户名字
        self.enter(LoginRoom(self.server))

    def enter(self, room):
        # 从当前房间移除自身,然后添加到指定房间
        try:
            cur = self.room
        except AttributeError:
            pass
        else:
            cur.remove(self)
        self.room = room
        room.add(self)

    def collect_incoming_data(self, data):
        self.data.append(data.decode())

    def found_terminator(self):
        '''
        当客户端的一条数据结束时调用
        :return: 
        '''
        line = ''.join(self.data)  # 将消息记录拼接成一个字符串
        self.data = []
        try:
            self.room.handle(self, line.encode())
        except EndSession:
            self.handle_close()

    def handle_close(self):
        asynchat.async_chat.handle_close(self)
        self.enter(LogoutRoom(self.server))


class CommandHandler:

    def handle(self, session, line):
        line = line.decode()
        if not line.strip():
            return

        parts = line.split(' ', 1)
        cmd = parts[0]  # do_login msg
        try:
            line = parts[1].strip()
        except IndexError:
            line = ''

        method = getattr(self, 'do_' + cmd, None)
        try:
            method(session, line)
        except TypeError:
            pass


class Room(CommandHandler):
    '''
    包含多个用户环境, 负责基本的命令处理和广播消息
    '''

    def __init__(self, server):
        self.server = server
        self.sessions = []  # 记录在线人数

    def add(self, session):
        self.sessions.append(session)

    def remove(self, session):
        self.sessions.remove(session)

    def broadcast(self, line):
        for session in self.sessions:
            session.push(line)

    def do_logout(self, session, line):
        raise EndSession


class ChatRoom(Room):
    '''
    代表正在聊天的客户
    '''

    def add(self, session):
        session.push(b'login success')  # 发送给客户端
        self.broadcast((session.name + ' has entered the room. \n').encode())
        self.server.users[session.name] = session
        Room.add(self, session)

    def remove(self, session):
        Room.remove(self, session)
        self.broadcast((session.name + ' has left the room.\n').encode())

    def do_say(self, session, line):
        self.broadcast((session.name + ':' + line + '\n').encode())

    def do_look(self, session, line):
        '''
        查询在线用户
        :param session: 
        :param line: 
        :return: 
        '''
        session.push(b'Online Users:\n')
        for user in self.sessions:
            session.push((user.name + '\n').encode())


class LoginRoom(Room):

    def add(self, session):
        Room.add(self, session)
        session.push(b'Connect Success')

    def do_login(self, session, line):
        name = line.strip()
        if not name:
            session.push(b'Username empty')
        elif name in self.server.users:
            session.push(b'Username exists')
        else:
            session.name = name
            session.enter(self.server.main_room)


class LogoutRoom(Room):
    '''
    处理退出的用户
    '''

    def add(self, session):
        try:
            del self.server.users[session.name]
            print('用户{}离线啦'.format(session.name))
        except KeyError:
            print('User does not exists')


if __name__ == '__main__':
    PORT = 6666
    ChatServer(PORT)
    print('服务器已经在0.0.0.0:{}开始运行...'.format(PORT))
    asyncore.loop()

聊天室GUI客户端

'''
聊天室客户端1
'''

import PySimpleGUI as sg
import telnetlib

def login(conn):
    is_login = False
    layout = [
        [sg.Text('登录聊天室')],
        [sg.Text('请输入您的用户名:'), sg.InputText(key='-username-'), sg.Button('进入聊天室')]
    ]

    window = sg.Window('聊天室', layout)
    while True:
        event, values = window.read()
        if event == '进入聊天室': # 如果用户点击了进入聊天室的按钮
            username = values['-username-'].strip()  ## 获取了InputText里面用户输入的用户名
            if username:
                try:
                    conn.open('127.0.0.1', 6666, timeout=10)
                except:
                    sg.popup('连接失败,请检查服务器设置')
                    break
                resp = conn.read_some()  # 读取服务器的响应信息
                if resp != b'Connect Success':
                    sg.popup('连接失败,请检查服务器设置')
                    break
                conn.write(('login ' + username + '\n').encode())   # 向服务器发送登录请求
                resp = conn.read_some()
                if resp == b'Username exists':
                    sg.popup('用户名已存在')
                else:
                    sg.popup('登录成功')
                    is_login = True
                    break
            else:
                sg.popup('请输入用户名')
        if event in (None, 'Cancel'):
            break

    window.close()
    del window

    if is_login:
        chat(conn, username)

def chat(conn, username):
    layout = [
        [sg.Text('聊天窗口(' + username + ')')],
        [sg.Multiline(disabled=True, size=(80, 20), key='-chathistory-')],
        [sg.InputText(size=(70, 5), key='-message-'), sg.Button('发送消息')],
        [sg.Button('查询在线人数'), sg.Button('退出聊天室')]
    ]
    window = sg.Window('Python聊天室', layout)

    while True:
        event, values = window.read(timeout=10)  # timeout参数表示当前是异步周期性更新模式
        if values:
            history_msg = values['-chathistory-']
            result = conn.read_very_eager().decode()
            if result.strip():
                if 'Online' in result:
                    sg.popup('当前在线用户', result)
                else:
                    window['-chathistory-'].update(history_msg + result.strip())
            if event in (None, '退出聊天室'):
                break
            elif event == '发送消息':
                send(conn, 'say', values['-message-'])
                window['-message-'].update('')
            elif event == '查询在线人数':
                send(conn, type='look')

    print('窗口即将关闭')
    conn.write(b'logout\n')
    conn.close()
    window.close()

def send(conn, type=None, msg=None):
    if type == 'say':
        conn.write(('say ' + msg + '\n').encode())
    elif type == 'look':
        conn.write(b'look\n')

if __name__ == '__main__':
    conn = telnetlib.Telnet()
    login(conn)

运行结果如图
在这里插入图片描述

最后感谢B站socket老师

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中,`asyncore`是一个用于异步网络编程的库。它提供了一个基于事件循环的轻量级框架,用于构建基于事件驱动的网络应用程序。 `asyncore`库使用单个线程来处理多个网络连接,并通过回调机制来处理网络事件。它基于Python的`select`模块或其他底层操作系统提供的I/O多路复用机制,实现了非阻塞的套接字操作。 使用`asyncore`库可以轻松实现TCP和UDP服务器和客户端,并且能够处理并发连接、数据接收和发送等操作。它提供了一些基本的类和方法,如`dispatcher`、`handle_accept()`、`handle_read()`、`handle_write()`等,用于派发和处理网络事件。 以下是一个简单的示例代码,演示了使用`asyncore`库创建一个简单的TCP服务器: ```python import asyncore import socket class EchoHandler(asyncore.dispatcher_with_send): def handle_read(self): data = self.recv(1024) if data: self.send(data) class EchoServer(asyncore.dispatcher): def __init__(self, host, port): asyncore.dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.bind((host, port)) self.listen(5) def handle_accept(self): sock, addr = self.accept() print(f"Accepted connection from {addr[0]}:{addr[1]}") handler = EchoHandler(sock) server = EchoServer('localhost', 8080) asyncore.loop() ``` 在上述代码中,`EchoHandler`类继承自`asyncore.dispatcher_with_send`,用于处理接收到的数据并将其返回给客户端。`EchoServer`类继承自`asyncore.dispatcher`,用于监听和接受客户端连接。 通过创建`EchoServer`对象,并调用`asyncore.loop()`函数进入事件循环,我们可以在指定的主机和端口上启动一个简单的TCP服务器。服务器将接受客户端连接,并将接收到的数据回传给客户端。 需要注意的是,`asyncore`库是Python标准库中的一部分,但它在处理大规模并发连接时可能性能不佳。在这种情况下,更推荐使用更高级的异步网络编程框架,如`asyncio`或第三方库`Twisted`。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值