本身動手打造mini型QQ(一):動手實現局域網仿QQ互聯

微信公衆號:inspurer
關注可瞭解更多的教程及排版技巧。問題或建議,請公衆號留言;
[若是你以爲對你有幫助,歡迎讚揚]php

這個項目的由來是來自計算機網絡課程學習的大做業,基於socket套接字寫一個超小型的QQ,晚上8點到12點的奮戰,編碼工做大體作完了,GUI框架也有了,特此分享出來。python

功能介紹

已完成

  • 支持單人聊天、支持羣聊(全部的人都在一個羣)編程

  • 支持單人收發文件、羣收發文件json

  • 多線程實現併發服務器

  • 人性化的UI界面微信

To do list

  • 給每一個ip維護一個暱稱,方便聊天網絡

  • 支持單人收發文件、羣收發文件多線程

  • select實現併發併發

操做說明

如圖所示:
app

在這裏插入圖片描述
  • 左邊是用戶框架,右邊是消息框架

  • 選擇`已登陸用戶`,消息/文件是羣發的

  • 選擇樹分支下的某個ip,消息/文件是私發給這個ip的

  • `消息``文件`二選一便可發送,優先發送消息

主要技術點

  • socket編程,實現點對點通訊

  • 消息格式統一採用json格式,統一打包和解析

  • wxPython打造GUI界面

  • 多線程編程、函數式編程

主要代碼

採用python環境編寫,pycharm+python3.5.1環境;

下面僅給出主要代碼

服務端server.py

def socketHander(connectionSocket):
    global connectionSocketList
    connectionSocketList.append(connectionSocket)
    connectionSocket.settimeout(2)
    for socket in connectionSocketList:
        socket.send(json.dumps(updateConnectionList()).encode("utf-8"))
    while True:
        try:
            # 接收消息
            receivedMessage = connectionSocket.recv(1024)
            if not receivedMessage:
                time.sleep(1)
                continue
            receivedMessage = receivedMessage.decode("utf-8")
            receivedMessage = json.loads(receivedMessage)
            print(receivedMessage)

            type = receivedMessage.get("type")


if __name__ == "__main__":
    serverSocket = socket(AF_INET,SOCK_STREAM)
    serverSocket.bind((serverIp,serverPort))
    serverSocket.listen(100)
    while True:
        connectionSocket,addr = serverSocket.accept()
        print(connectionSocket.getpeername()) #('127.0.0.1', 1958)
        Thread(target=socketHander,args=(connectionSocket,)).start()

客戶端client.py

def socketHander(self):
    self.clientSocket = socket(AF_INET, SOCK_STREAM)
    self.clientSocket.connect((serverIp, serverPort))
    self.clientSocket.settimeout(2)
    self.ip,self.port = self.clientSocket.getsockname()
    print("self ip",self.ip)
    while True:
        #發送消息
        if len(self.sendMessage) == 0:
            pass
        else:
            if self.isChoosedFile == True:
                self.clientSocket.send(json.dumps(self.sendMessage).encode("utf-8"))
                self.messageList.AppendText("文件[" + self.fileName + "]發送成功\r\n")
                self.fileName = None
                self.dataOfChoosedFile = None
                self.isChoosedFile = False
                self.sendMessage = ""

            else:
                self.clientSocket.send(json.dumps(self.sendMessage).encode("utf-8"))
                self.messageList.AppendText("消息["+self.sendMessage.get("content")+"]發送成功\r\n")
                self.input.SetLabelText("")
                self.sendMessage = ""

        try:
            # 接收消息
            receivedMessage = self.clientSocket.recv(1024)
            receivedMessage = receivedMessage.decode("utf-8")
            receivedMessage = json.loads(receivedMessage)
            print(receivedMessage)
            type = receivedMessage.get("type")

            # 客戶端接收服務端發來的轉發消息
            if type == "1":
                print("客戶端收到消息")
                sourceIp = receivedMessage.get("sourceIP")
                content = receivedMessage.get("content")
                if sourceIp == self.ip:
                    pass
                else:
                    self.messageList.AppendText("來自:["+sourceIp+"]的消息:["+content+"]\r\n")

            elif type == "2":
                # 客戶端接收服務端發來的刷新列表請求
                self.userList = receivedMessage.get("content")
                self.setUserList()

            elif type == "3":
                filename = receivedMessage.get("filename")
                print("rrrr",filename)
                with open(filename,"w"as f:
                    f.write(receivedMessage.get("content"))
        except:
            print("等待數據...")
            pass
    pass

def setUserList(self):
    self.userListTree.DeleteChildren(self.rootID)
    for user in self.userList:
        # if user == self.ip:
        #     continue
        self.userListTree.AppendItem(self.rootID,user)
    pass

函數說明

函數名稱 函數功能
socket(param1,param2) 建立一個套接字,param1指明網絡層協議,經常使用AF_INET(ip協議);param2指明傳輸層協議,經常使用SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)
bind((param1,param2)) 綁定socket到指定的ip(param1)和端口(param2),注意param1,param2必須組成一個元組
listen(param) 指明服務端最大的客戶端鏈接數
connect((param1,param2)) 客戶端鏈接到指定的服務端,參數同bind()
accept() 無參數,服務端接收來自客戶端的鏈接請求

關於配置兩臺PC的鏈接過程,我已經將過程紀錄於此:局域網下兩臺PC機互聯填坑之路

下一篇,咱們將考慮將服務端部署到阿里雲服務器,突破局域網的限制,隨時隨地聊天。


本文分享自微信公衆號 - 月小水長(inspurer)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索