网络聊天器,前端wxpython,后端c++

前端代码

import wx
import socket
import threading
import random

from wx import TE_MULTILINE


class chatFrame(wx.Frame):
    def __init__(self,nickname):
        super().__init__(parent=None,title="多人聊天室",size=(600,400))
        self.nickname = nickname
        self.client_socket = None
        self.initSocket()
        self.initUI()
        self.Center()
        self.Show()
    def initSocket(self):
        try:
            self.client_socket=socket.socket()
            self.client_socket.connect(('123.56.19.236',8080))
            self.client_socket.send(bytes(self.nickname,encoding='utf8'))
            threading.Thread(target=self.recv_msg, daemon=True).start()
        except Exception as e:
            wx.MessageBox(f'连接聊天室失败:{e}','错误',wx.ICON_EXCLAMATION)
            self.Close()

    def initUI(self):
        panel=wx.Panel(self)
        v_sizer = wx.BoxSizer(wx.VERTICAL)  # 垂直布局
        self.chatItf=wx.TextCtrl(
            parent=panel,size=(580,300),
            #readonly只读
            style=TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL|wx.VSCROLL #此处查资料的,书上没有,就是用来聊天记录滑动的
        )
        v_sizer.Add(self.chatItf,0,wx.ALL | wx.EXPAND, 10)
        h_sizer=wx.BoxSizer(wx.HORIZONTAL)
        self.inputMsg=wx.TextCtrl(parent=panel,size=(480,30))
        btn=wx.Button(parent=panel,label="发送", size=(80, 30))
        #这个bind坑点:绑定的方法必须有两个参数,他会默认传递一个event(事件说明书)
        btn.Bind(wx.EVT_BUTTON, self.send_msg)
        h_sizer.Add(self.inputMsg, 0,  wx.EXPAND,0)
        h_sizer.Add(btn,0,wx.EXPAND,0)
        #将水平的sizer垂直放在垂直sizer的下面
        v_sizer.Add(h_sizer, 0,wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, 10) #我操,这里一定不要用wx.ALL,不然文本框和按钮就直接被边距挤没了
        panel.SetSizer(v_sizer)
    def recv_msg(self):
        while True:
            msg=self.client_socket.recv(1024).decode('utf8')#接受到的消息一定要记得解码
            self.chatItf.AppendText(str(msg)+'\n')#记得换行
    def send_msg(self,event):#不写event直接报错,参数不匹配
        msg=self.inputMsg.GetValue()
        #这里要记得判空,别把自己名字发过去了
        if not msg:
            return
        self.chatItf.AppendText(f'[{self.nickname}]#{msg}\n')#要记得换行
        self.client_socket.send(bytes(msg,encoding='utf8'))
        self.inputMsg.SetValue('')
#程序运行主逻辑

def main():
    app=wx.App()
    dlg=wx.TextEntryDialog(parent=None,message='请输入你的昵称',caption='用户-昵称',value='用户'+str(int(random.random()*1000)))
    if dlg.ShowModal()==wx.ID_OK:
        nickname=dlg.GetValue()
        if not nickname:
            nickname = "匿名用户"
        frame=chatFrame(nickname)
    dlg.Destroy()
    app.MainLoop()
if __name__=="__main__":
    main()

c++后端

#include<iostream>
#include<cstdio>
#include<cstring>
#include<unistd.h>
#include<pthread.h>
#include<sys/socket.h>
#include<sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<arpa/inet.h>
#include<sys/wait.h>
#include<cstdlib>
#include<vector> 
using namespace std;

vector<int> client_fds;

void* task(void * arg) {
    int client_fd = (int)(long)arg;
    char nickname[128] = {0};
    char recv_buf[1024] = {0};
    ssize_t recv_len = recv(client_fd, nickname, sizeof(nickname)-1, 0);
    if (recv_len <= 0) { 
        close(client_fd);
        return nullptr;
    }
    nickname[recv_len] = '\0';
    client_fds.push_back(client_fd);

    char join_msg[256] = {0};
    snprintf(join_msg, sizeof(join_msg), "[系统]# %s 加入了聊天室!", nickname);
    for (int fd : client_fds) {
        if (fd != client_fd) {
            send(fd, join_msg, strlen(join_msg), 0);
        }
    }

    while (true) {
        memset(recv_buf, 0, sizeof(recv_buf));
        recv_len = recv(client_fd, recv_buf, sizeof(recv_buf)-1, 0);
        if (recv_len <= 0) {
            for (auto it = client_fds.begin(); it != client_fds.end(); ++it) {
                if (*it == client_fd) {
                    client_fds.erase(it);
                    break;
                }
            }
            char leave_msg[256] = {0};
            snprintf(leave_msg, sizeof(leave_msg), "[系统]# %s 离开了聊天室!", nickname);
            for (int fd : client_fds) {
                send(fd, leave_msg, strlen(leave_msg), 0);
            }
            close(client_fd);
            break;
        }

        char broadcast_msg[1024] = {0};
        snprintf(broadcast_msg, sizeof(broadcast_msg), "[%s]# %s", nickname, recv_buf);
        for (int fd : client_fds) {
            if (fd != client_fd) {
                send(fd, broadcast_msg, strlen(broadcast_msg), 0);
            }
        }
    }

    return nullptr;
}

int main(){
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) {
        perror("socket create failed");
        exit(EXIT_FAILURE);
    }

    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(8080); 
    local.sin_addr.s_addr = htonl(INADDR_ANY); 
    if (bind(listenfd, (struct sockaddr*)&local, sizeof(local)) < 0) {
        perror("bind failed");
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    if (listen(listenfd, 5) < 0) {
        perror("listen failed");
        close(listenfd);
        exit(EXIT_FAILURE);
    }
    printf("服务端启动成功,监听端口 8080\n");

    while(true) {
        struct sockaddr_in src;
        socklen_t len = sizeof(src);
        int client_fd = accept(listenfd, (struct sockaddr*)(&src), &len);
        if (client_fd < 0) {
            perror("accept failed");
            continue;
        }
        printf("客户端[%s:%d] 连接成功\n", inet_ntoa(src.sin_addr), ntohs(src.sin_port));
        pthread_t tid;
        // 这里一定要记得:int转void*需转long,避免64位系统截断
        int n = pthread_create(&tid, nullptr, task, (void*)(long)client_fd);
        if (n != 0) {
            perror("pthread_create failed");
            close(client_fd);
            continue;
        }
        pthread_detach(tid);
    }

    close(listenfd);
    return 0;
}
posted @ 2025-12-19 11:53  xdhking  阅读(8)  评论(0)    收藏  举报