一. 接着上一篇文章,原理:多个客户端在线,服务器端会产生多个连接客户端的线程,通过Queue使这些连接客户端线程相互发送消息
二 .新增三个protobuf数据文件,当然socket传输数据也可以用其他格式数据,如json等,我的前几篇文章都是用protobuf格式数据传输的。
FriendMessage.py

syntax = "proto3";
message FriendMessage {
    string FriendId = 1; // ID号
    string FriendNo = 2; // 别名
    string FriendNick = 3; // 好友的昵称
    string Avatar = 9; //头像 
}

ContactsInfoNoticeMessage.py

syntax = "proto3";
import "FriendMessage.proto";
message ContactsInfoNoticeMessage {
    string WeChatId = 1; // 全局唯一识别码
    repeated FriendMessage Friends = 2; // 好友信息
}
FriendTalkNoticeMessage.py syntax = “proto3”;
 message FriendTalkNoticeMessage {
 string WeChatId = 1; // 唯一识别码
 string FriendId = 3; // 好友唯一识别码
 bytes Content = 6; // 内容 二进制流
 }

编译生成的.py文件复制到项目中,如果不知道怎么编译请参考我的第二篇文章

三.服务器端代码,ServerProtoTest.py

import socket
import time
import google.protobuf
import google.protobuf.any_pb2
import TransportMessage_pb2
import WeChatOnlineNoticeMessage_pb2
import ContactsInfoNoticeMessage_pb2
import FriendMessage_pb2
import FriendTalkNoticeMessage_pb2
from queue import Queue
from threading import Thread
import threading
import gevent
from gevent import socket,monkey
monkey.patch_all()
import socketserver

allFriendList = list()  #不包含重复的好友信息
List_Lock = threading.Lock() #线程锁
queue = Queue()#创建队列
"""class Myserver(socketserver.BaseRequestHandler):

    def handle(self):
        conn = self.request
        Thread_recv(conn)"""

def Thread_send(socket_Server,queue,WeChatId):
    global allFriendList
    try:
        while True:
            msg = queue.get()
            print('Thread_send',msg['FriendId'],WeChatId)
            if msg['FriendId']==WeChatId:
                SendMessage = TransportMessage_pb2.TransportMessage()
                FriendTalkMessage = FriendTalkNoticeMessage_pb2.FriendTalkNoticeMessage()
                FriendTalkMessage.Content = msg['Content']
                FriendTalkMessage.WeChatId = WeChatId

                SendMessage.MsgType = 1025
                SendMessage.Id = 0
                SendMessage.AccessToken ="ac897dss"
                SendMessage.Content.Pack(FriendTalkMessage)
                byte_data = SendMessage.SerializeToString()
                byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                socket_Server.send(byte_head)
                socket_Server.send(byte_data)
            else:

                for friend in allFriendList:
                    if friend['WeChatId'] == msg['FriendId']:
                        queue.put(msg)
                        time.sleep(0.000001)#只要有个延时就行,不然会导致代码死循环
                

    except Exception as ex:
        print('Thread_send 异常...',ex)

def Thread_recv(socket_Server):
    try:
        global allFriendList

        global queue

        online = WeChatOnlineNoticeMessage_pb2.WeChatOnlineNoticeMessage()
        while True:
            Head_data = socket_Server.recv(4)  #接收数据头 4个字节,
            data_len = int.from_bytes(Head_data, byteorder='big')
            print('data_len=',data_len)
            protobufdata = socket_Server.recv(data_len)
            
            tmessage = TransportMessage_pb2.TransportMessage()
            tmessage.ParseFromString(protobufdata)
            

            i_id = tmessage.Id
            i_msgtype = tmessage.MsgType
            now_time = time.strftime('%Y-%m-%d %H:%M:%S')
            print(now_time,' id:',i_id,'msgType:',i_msgtype)
            if i_msgtype==0:
                print('异常')
                for friend in allFriendList:
                    if friend['WeChatId'] == online.WeChatId:
                        print('下线WeChatId:',online.WeChatId)
                        if List_Lock.acquire():
                            allFriendList.remove(friend)
                            List_Lock.release()
                            break
                socket_Server.close()
                break
            if i_msgtype==1010:
                print(now_time,' 服务器接收到心跳包...好友在线个数:',len(allFriendList))
                #print(now_time,' 服务器接收到心跳包...')
            if i_msgtype == 1020:
                print(now_time,' 服务器接收到上线通知...')
                #有上线通知,将好友信息和socket_Server套接字存放在buf中,
                #这样可以将好友A的信息转发给好友B,做成及时聊天工具
                #有下线通知,将好友信息和socket_Server套接字从buf中去掉
                #服务器端功能就是收集上线和下线通知,转发聊天消息
                
                tmessage.Content.Unpack(online)

                print(now_time,' WeChatNo:'+online.WeChatNo,'WeChatId:'+online.WeChatId,'WeChatNick:'+online.WeChatNick)
                
                #没有添加就添加,列表中有的话就更新
                f_dict = {"WeChatId":online.WeChatId,"socket_Server":socket_Server,"WeChatOnlineNoticeMessage":online}
                isAddFriend = True
                for friend in allFriendList:
                    if friend['WeChatId'] == online.WeChatId:
                        isAddFriend = False
                        print('已上线WeChatId:',online.WeChatId)
                        
                if isAddFriend:
                    if List_Lock.acquire():
                        allFriendList.append(f_dict)
                        List_Lock.release()
                        #新建转发好友消息的线程
                        t_send = threading.Thread(target=Thread_send,args=(socket_Server,queue,online.WeChatId))
                        t_send.start()



            if i_msgtype == 1021:
                print(now_time,'服务器接收到下线通知')
                for friend in allFriendList:
                    if friend['WeChatId'] == online.WeChatId:
                        print('下线WeChatId:',online.WeChatId)
                        if List_Lock.acquire():
                            allFriendList.remove(friend)
                            List_Lock.release()
                            break
                socket_Server.close()
                break #退出while
            if i_msgtype == 1022:
                print(now_time,'服务器接收到客户端请求在线好友信息通知')
                contacts = ContactsInfoNoticeMessage_pb2.ContactsInfoNoticeMessage()
                for friend in allFriendList:
                    friendmessage = contacts.Friends.add()
                    friendmessage.FriendId = friend['WeChatId']
                    #contacts.add(friendmessage)
                transportMessage = TransportMessage_pb2.TransportMessage()#注意括号不要掉了,
                transportMessage.MsgType = 1023
                transportMessage.Id = 0
                transportMessage.AccessToken ="ac897dss"
                transportMessage.Content.Pack(contacts)

                byte_data = transportMessage.SerializeToString()
                byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                socket_Server.send(byte_head)
                socket_Server.send(byte_data)
            if i_msgtype == 1024:
                print(now_time,'转发好友聊天内容')
                FriendTalkMessage = FriendTalkNoticeMessage_pb2.FriendTalkNoticeMessage()
                tmessage.Content.Unpack(FriendTalkMessage)
                print(now_time,online.WeChatId,FriendTalkMessage.FriendId,str(FriendTalkMessage.Content,'utf-8'))
                #if FriendTalkMessage.FriendId == online.WeChatId:
                
                msg ={'FriendId':FriendTalkMessage.FriendId,'Content':FriendTalkMessage.Content}
                
                queue.put(msg)

                """SendMessage = TransportMessage_pb2.TransportMessage()#注意括号不要掉了,
                SendMessage.MsgType = 1025
                SendMessage.Id = 0
                SendMessage.AccessToken ="ac897dss"
                SendMessage.Content.Pack(FriendTalkMessage)
                byte_data = SendMessage.SerializeToString()
                byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                socket_Server.send(byte_head)
                socket_Server.send(byte_data)"""


    except Exception as ex:
        print('Thread_recv 异常...',ex)
        for friend in allFriendList:
                    if friend['WeChatId'] == online.WeChatId:
                        print('下线WeChatId:',online.WeChatId)
                        if List_Lock.acquire():
                            allFriendList.remove(friend)
                            List_Lock.release()
                            break
    finally:
        socket_Server.close()


if __name__ == "__main__":
    
     #方法3
    #server = socketserver.ThreadingTCPServer(('192.168.0.100',11087),Myserver)
    #server.serve_forever()

    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    HostPort = ('192.168.0.12',11087)
    s.bind(HostPort)  #绑定地址端口
    s.listen(100)  #监听最多100个连接请求
    
    while True:
        print('server socket waiting...')
        
        obj,addr = s.accept()  #阻塞等待链接
        print('socket object:',obj)

        print('client info:',addr)
        
        
        #方法1
        t_recv = threading.Thread(target=Thread_recv,args=(obj,))
        t_recv.start()

        #方法2
        #gevent.spawn(Thread_recv,obj)

四.客户端代码,ClientProtoTest.py

from socket import *
import google.protobuf
import TransportMessage_pb2
import WeChatOnlineNoticeMessage_pb2
import ContactsInfoNoticeMessage_pb2
import FriendTalkNoticeMessage_pb2
import traceback
import google.protobuf.any_pb2
import threading
import time

Id = 0
IsConnect = False
onlineNotice_bytes=None
WeChatId = None
contacts = None

def HearBeatReq_bytes():
    global Id
    Id+=1
    transportMessage = TransportMessage_pb2.TransportMessage()#注意括号不要掉了,
    transportMessage.MsgType = 1010
    transportMessage.Id = Id
    transportMessage.AccessToken ="ac897dss"
    #print('心跳包数据...Id=',Id)
    return transportMessage.SerializeToString()
def OfflineNotice_bytes():
    global Id
    Id+=1
    transportMessage = TransportMessage_pb2.TransportMessage()#注意括号不要掉了,
    transportMessage.MsgType = 1021
    transportMessage.Id = Id
    transportMessage.AccessToken ="ac897dss"
    print('下线通知...Id=',Id)
    return transportMessage.SerializeToString()

def OnlineNotice_bytes():
    global Id
    global WeChatId
    time_t = int(time.time())
    
    WeChatId = "id_"+str(time_t) #随机生成一个WeChatId
    print(WeChatId)
    Id+=1
    transportMessage = TransportMessage_pb2.TransportMessage()#注意括号不要掉了,
    transportMessage.MsgType = 1020
    transportMessage.Id = Id
    transportMessage.AccessToken ="ac897dss"

   
    weChatOnlineNotice = WeChatOnlineNoticeMessage_pb2.WeChatOnlineNoticeMessage()
    weChatOnlineNotice.WeChatId = WeChatId
    weChatOnlineNotice.WeChatNo = "qdj_cancle"
    weChatOnlineNotice.WeChatNick = "昵称001"
    weChatOnlineNotice.Gender = 0
    weChatOnlineNotice.Country = "中国"
    print('上线通知...Id=',Id)
    transportMessage.Content.Pack(weChatOnlineNotice)

    return transportMessage.SerializeToString()
def GetOnlineFriendNotice_bytes():
    global Id
    Id+=1
    transportMessage = TransportMessage_pb2.TransportMessage()#注意括号不要掉了,
    transportMessage.MsgType = 1022
    transportMessage.Id = Id
    transportMessage.AccessToken ="ac897dss"
    print('获取在线好友通知...Id=',Id)
    return transportMessage.SerializeToString()
def SendChatContentNotice_bytes(FriendId,content):
    global Id
    global WeChatId
    Id+=1
    #print(FriendId,content)
    transportMessage = TransportMessage_pb2.TransportMessage()#注意括号不要掉了,
    transportMessage.MsgType = 1024
    transportMessage.Id = Id
    transportMessage.AccessToken ="ac897dss"
    FriendTalkNotice = FriendTalkNoticeMessage_pb2.FriendTalkNoticeMessage()
    FriendTalkNotice.WeChatId = WeChatId #随机生成一个WeChatId
    FriendTalkNotice.FriendId = FriendId
    FriendTalkNotice.Content = bytes(content,'utf-8')
    transportMessage.Content.Pack(FriendTalkNotice)


    print(WeChatId,'给好友',FriendId,'发送消息',content)
    return transportMessage.SerializeToString()

def thread_HearBeat(tcpCliSock):
    global IsConnect 
    try:
        t = 1
        print('thread_HearBeat=',IsConnect)
        while IsConnect:
            if t > 10:
                t = 1
                hearBeat_data = HearBeatReq_bytes()
                byte_data = hearBeat_data
                byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                tcpCliSock.send(byte_head)
                tcpCliSock.send(byte_data)
                #print('10秒定时发送心跳包...\r\n发送数据[0].退出;[1].心跳包;[2].上线通知>')
            time.sleep(1)
            t+=1

    except Exception as ex:
        print('tcpCliSock异常',ex)
    finally:
        tcpCliSock.close()
def thread_recv(tcpCliSock):
    global IsConnect 
    global contacts
    try:
        while IsConnect:
            Head_data = tcpCliSock.recv(4)  #接收数据头 4个字节,
            data_len = int.from_bytes(Head_data, byteorder='big')
            print('data_len=',data_len)
            protobufdata = tcpCliSock.recv(data_len)
            
            tmessage = TransportMessage_pb2.TransportMessage()
            tmessage.ParseFromString(protobufdata)
            

            i_id = tmessage.Id
            i_msgtype = tmessage.MsgType
            now_time = time.strftime('%Y-%m-%d %H:%M:%S')
            print(now_time,' id:',i_id,'msgType:',i_msgtype)
            if i_msgtype==0:
                print('异常')
                break
            if i_msgtype==1023:
                print(now_time,' 接收到在线好友消息:')
                print(tmessage.Content)
                contactsMessage = ContactsInfoNoticeMessage_pb2.ContactsInfoNoticeMessage()
                tmessage.Content.Unpack(contactsMessage)
                contacts = contactsMessage.Friends
                index = 0
                for friend in contacts:
                    index+=1
                    print(index,'在线好友Id:'+friend.FriendId)
            if i_msgtype == 1025:
                print(now_time,'接收到好友消息:')
                FriendTalkMessage = FriendTalkNoticeMessage_pb2.FriendTalkNoticeMessage()
                tmessage.Content.Unpack(FriendTalkMessage)
                print(now_time,FriendTalkMessage.FriendId,'发来消息:'+str(FriendTalkMessage.Content,'utf-8'))



    except Exception as ex:
        print('thread_recv异常',ex)
    finally:
        tcpCliSock.close()

def main():
    global IsConnect 
    global onlineNotice_bytes
    global WeChatId
    HOST = '192.168.0.12'
    PORT = 11087
    BUFSIZ =1024
    ADDR = (HOST,PORT)

    tcpCliSock = socket(AF_INET,SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    IsConnect = True

    t_hearBeat = threading.Thread(target=thread_HearBeat,args=(tcpCliSock,))
    t_hearBeat.start()  #启动心跳线程
    t_rev = threading.Thread(target=thread_recv,args=(tcpCliSock,))
    t_rev.start()  #启动心跳线程
    #friendInfoList = list()
    try:
        while IsConnect:
        
            data1 = input('[0].下线退出;\n[1].心跳包;\n[2].上线通知;\n[3].获取在线好友信息;\n[4].给所有在线好友发送消息;\n>')
            print('输入指令:',data1)
            if data1=='0':
                Offline_data = OfflineNotice_bytes()
                byte_data = Offline_data
                byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                tcpCliSock.send(byte_head)
                tcpCliSock.send(byte_data)
                IsConnect = False
                break
            if data1 =='1':
                hearBeat_data = HearBeatReq_bytes()
                byte_data = hearBeat_data
                byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                tcpCliSock.send(byte_head)
                tcpCliSock.send(byte_data)
            if data1 =='2':
                if onlineNotice_bytes is None:
                    onlineNotice_bytes = OnlineNotice_bytes()
                byte_data = onlineNotice_bytes
                byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                tcpCliSock.send(byte_head)
                tcpCliSock.send(byte_data)
            if data1 == '3':
                byte_data = GetOnlineFriendNotice_bytes()
                byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                tcpCliSock.send(byte_head)
                tcpCliSock.send(byte_data)
            if data1 == '4':
                if not contacts is None:
                    for friend in contacts:
                        if not WeChatId==friend.FriendId:
                            byte_data = SendChatContentNotice_bytes(friend.FriendId,'hello,我是'+WeChatId)
                            byte_head = (len(byte_data)).to_bytes(4, byteorder='big')
                            tcpCliSock.send(byte_head)
                            tcpCliSock.send(byte_data)
                        

    except Exception as identifier:
        print('退出程序!',IsConnect)
    finally:
        tcpCliSock.close()
    
if __name__ == "__main__":
    main()

五.运行结果,开启三个客户端,输入数字2,上线三个客户端

python如何发短信 python发送信息_socket


聊天运行结果如下:客户端输入数字3,获取在线好友信息

python如何发短信 python发送信息_python如何发短信_02


客户端输入数字4,发送消息给其他两个好友,结果如下:

python如何发短信 python发送信息_python_03


总结:此项目到此为止,不再进行扩展了,1到7篇文章都是描述这个项目的,通过socket传输protobuf结构数据,实现客户端相互聊天功能。