IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> python socket基于TCP/IP协议实现多人聊天室 -> 正文阅读

[网络协议]python socket基于TCP/IP协议实现多人聊天室

前言

所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。

img

socket 位于网络层以及传输层之间,它提供了三种类型的传输:

  1. 流套接字(SOCK_STREAM)
  2. 数据报套接字(SOCK_DGRAM)
  3. 原始套接字(SOCK_RAW)

第一种使用了TCP协议进行数据的传输,在传输之前需要建立连接,第二种使用UDP协议,传输数据不需要建立连接,本文采用TCP协议,也就是流套接字。

一、实现原理

一对一聊天室:服务端建立连接,等待连接,客户端连接服务端,实现收发。注意,此时通讯为半双工通信

多用户聊天室:服务端建立连接,等待连接,客户端连接服务端,服务器为每个连接上的客户端开启子线程,同时监听每个客户端发送的消息,所有子线程监听到的客户端消息加入Queue队列,另外开启一个子线程用于监听队列消息,并转发给其它所有客户端,主线程用于监听总人数。同样地,客户端接收消息也单独开启一个子线程,用于同步接收。此时通讯为全双工通信

半双工:在某一时刻,只允许数据在单方向传输。也就是只能一边发一边收,发送的时候不能同时接收。

全双工: 允许数据同时在两个方向传输,即 发送的同时也可以接收接收的同时也可以发送

二、queue队列

python 的queue 队列主要用于解决 两个问题:

1.线程对公共资源占用的问题。避免了A操作资源时候B同时进行操作,发生程序错误。

2.资源重复的问题,queue队列中的元素被取出后就不可重复取出。

import queue

q = queue.Queue()
for i in range(20):
    q.put(i)

for i in range(20):
    if q.empty()==False:
        print(q.get_nowait())

print(q.get_nowait())

上述代码,运行结果为:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Traceback (most recent call last):
  File "c:\Python文件夹\空白测试.py", line 11, in <module>
    print(q.get_nowait())
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python39\lib\queue.py", line 199, in get_nowait
    return self.get(block=False)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python39\lib\queue.py", line 168, in get
    raise Empty
_queue.Empty

结果可知,queue除了代替线程锁的功能,还避免了资源复制重复使用。

三、代码实现

服务端:

import socket
import queue
import threading
import time

serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
host = socket.gethostname()
print(socket.gethostbyname(host))
serversocket.bind((host, 9090))
serversocket.listen(5)
#存放已连接的对象
clients = []
#存放公共消息的容器
public_message = dict()

#接收新的对象
def init():
    while True:    
        client,addr = serversocket.accept()  #阻塞线程
        if client in clients:
            print('老用户')
        else:
            print('新的用户加入:',end='')
            print(client.getpeername()[0])
            client.send(bytes('欢迎来到匿名聊天室'.encode('utf-8')))
            clients.append(client)
            r = threading.Thread(target=receive_msg,args=(client,))
            r.start()
        

#接收消息
def receive_msg(client):
    while True:
        try:
            if client in clients:
                data = client.recv(1024).decode('utf-8')
                if data!='':
                    print(data)
                    public_message[client] = queue.Queue()
                    public_message[client].put(data)

        except BaseException as error:

            print('用户强制中断了一个连接')
            # print('错误:',error)
            if client in clients:
                clients.remove(client)


#转发消息(非阻塞)
def broadcast():
    while True:
        if len(clients)>1:
            for client in clients:
                public_message_clone = [i for i in public_message]   #解决字典迭代中操作报错的问题
                for i in public_message_clone:
                    if i!=client and public_message[i].empty()==False:
                        data = public_message[i].get_nowait()
                        if data !='':
                            client.send(bytes(data.encode('utf-8')))
                            print('服务器转发了消息')
         
                    
t1 = threading.Thread(target=init)
t2 = threading.Thread(target=broadcast)

t1.start()
t2.start()


#主线程监听在线人数
while (True):
    print("当前在线人数为:%d"%(len(clients)))
    time.sleep(5)

客户端:

import socket,threading
#客户端想要发消息和收消息同时进行,需要使用多线程达到并发效果

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
host = socket.gethostname() 
s.connect((host, 9090))


def receive():
    while True:
        data = s.recv(1024).decode('utf-8')
        if data!='':
            print(data)
     
    
def send_msg():
    while (True):
        msg = input(':')
        if msg=='exit':
            s.close()
            break
        s.send(bytes(msg.encode('utf-8')))
        
t1 = threading.Thread(target=receive,daemon=True)
t1.start()

send_msg()

效果图
在这里插入图片描述

四、需要注意的地方

由于queue队列在元素被取出后会自动删除元素,如果queue队列作为字典的value建立在一个字典的键值对中,那么需要提前复制这个字典的所有key给另一个变量,避免在字典循环中自动删除元素导致了程序报错。

五、总结

在实现多人聊天程序之前,一定要思路清晰,提前设计好思路方法,否则容易处处报错,以上如有不当之处欢迎批评指正。

欢迎交流:添加QQ好友
添加微信好友:在这里插入图片描述

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-14 14:29:09  更:2021-08-14 14:29:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 15:32:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码