c++多线程实现的socket聊天室代码,可开多个客户端。


2024-05-27 更新

  1. 在这个程序中有些未定义行为,导致有的编译器运行过程中出现create后服务器没有收到消息。如评论所说。现已修复。
  2. 打开多个客户端可以在你创建的项目中找到 .exe可执行程序,多次运行就可以打开多个客户端。
  3. 极简版删除
  4. 若是用clion编辑器,使用cmake构建项目,在CMakeLists.txt文件中添加link_libraries(ws2_32)如下:
cmake_minimum_required(VERSION 3.14)
project(项目名)

link_libraries(ws2_32)

set(CMAKE_CXX_STANDARD 标准)

add_executable(项目名 main.cpp
        1.cpp)


代码很乱,不建议仔细研究,需要讲解可留评论。
运行截图:
在这里插入图片描述
Server:

#include<iostream>
#include<winsock2.h>
#include<process.h>
#include<windows.h>
#include<vector>
#include<thread>
#include<set>
#pragma comment(lib,"ws2_32.lib")
#define PORT 65432    //定义要访问的服务器端口常量
#define MSG_SHOW 1
#define MSG_CREATE 2
#define MSG_JOIN 3
#define MSG_LEAVE 4
#define MSG_QUIT 5
#define MSG_EXIT 5
#define MSG_TALK 6
//#pragma comment(lib,"ws2_32.lib")
using namespace std;
int writefile(char* num, HANDLE fileHandle);
DWORD d1 = 0;

class MsgHead {
public:
    int msgType;
    int	dataLength;
};
class MsgShow :public MsgHead
{
public:
    MsgShow() {
        msgType = MSG_SHOW;
        dataLength = sizeof(MsgShow);
    }

};
class MsgCreate :public MsgHead
{
public:
    MsgCreate() {
        msgType = MSG_CREATE;
        dataLength = sizeof(MsgCreate);
    }

};
class MsgJoin :public MsgHead
{

public:
    int number;
    MsgJoin(int number) {
        msgType = MSG_JOIN;
        dataLength = sizeof(MsgJoin);
        this->number = number;
    }
};
class MsgLeave :public MsgHead
{
public:
    MsgLeave() {
        msgType = MSG_LEAVE;
        dataLength = sizeof(MsgLeave);
    }

};
class MsgExit :public MsgHead
{
public:
    MsgExit() {
        msgType = MSG_QUIT;
        dataLength = sizeof(MsgExit);
    }

};
class MsgTalk :public MsgHead
{
    char buff[1024];
public:
    MsgTalk() {
        memset(buff, '\0', sizeof(buff));
        msgType = MSG_TALK;
        dataLength = sizeof(MsgTalk);
    }
    char* getBuff() {
        return buff;
    }
};
vector<set<SOCKET>> vecMsgCreate;
void dealWithData(MsgHead* msg,SOCKET sClient) {

    if (msg->msgType == MSG_SHOW) {
        cout << "show" << endl;
        int identifier = 1;
        for (vector<set<SOCKET>>::iterator it = vecMsgCreate.begin();it != vecMsgCreate.end();it++)
        {
            cout << "**********chat room: " << identifier <<" ********" << endl;
            cout << " users: " << endl;
            for (set<SOCKET>::iterator j = it->begin();j != it->end();j++)
            {
                cout <<' '<< * j << endl;
            }
            cout << "*****************************" << endl;
            identifier++;
        }
    }
    else if (msg->msgType == MSG_CREATE){
        cout << "create" << endl;
        set<SOCKET> S;
        S.insert(sClient);
        vecMsgCreate.push_back(S);

    }
    else if (msg->msgType ==  MSG_JOIN) {
        cout << "join" << endl;
        int number = ((MsgJoin*)msg)->number;
        vecMsgCreate[number - 1].insert(sClient);
        //vector<set<SOCKET>>::iterator it = vecMsgCreate[number - 1].begin();
    }
    else if (msg->msgType == MSG_LEAVE) {
        cout << "leave" << endl;
    }
    else if ((msg->msgType == MSG_QUIT ) || (msg->msgType ==  MSG_EXIT)) {
        cout << "quit" << endl;
    }
    else if (msg->msgType == MSG_TALK) {
        //cout << "开始聊天" << endl;
        //MsgTalk* msgtalk = (MsgTalk*)buff;
        char *s= ((MsgTalk*)msg)->getBuff();
        cout << s << endl;
        for (vector<set<SOCKET>>::iterator it = vecMsgCreate.begin();it != vecMsgCreate.end();it++)
        {
            if (it->find(sClient)!=it->end())
            {
                for (set<SOCKET>::iterator j = it->begin();j != it->end();j++)
                {

                    if (*j != sClient)
                    {
                        send(*j, (const char*)msg, 1000, 0);
                    }

                }
            }
        }

    }
}

int main()
{
    //MessageBox(NULL,TEXT("点击确定运行程序"), TEXT("hello world"),MB_OK);
    SOCKET sock_server, newsock;
    sockaddr_in addr, client_addr;
    unsigned hThread;


    /*初始化winsock DLL*/
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        cout << "加载winsock.dll失败!\n";
        return 0;
    }
    //data文件句柄
    HANDLE hfile = CreateFile(TEXT("..\\..\\data.txt"), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    SetFilePointer(hfile, 0, NULL, FILE_END);
    /*创建套接字*/
    //	if ((sock_server=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)

    if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
    {
        cout << "创建套接字失败!" << WSAGetLastError() << endl;
        WSACleanup();
        return 0;
    }

    /*绑定IP端口*/
    int addr_len = sizeof(struct sockaddr_in);
    memset((void*)&addr, 0, addr_len);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    /*给监听套接字绑定地址*/
//	if (bind(sock_server,(LPSOCKADDR)&addr,sizeof(addr))!=0)
    if (bind(sock_server, (const sockaddr *)&addr, sizeof(addr)) != 0)
    {
        cout << "绑定地址失败!" << WSAGetLastError() << endl;
        closesocket(sock_server);
        WSACleanup();
        return 0;
    }

    /*开始监听*/
    if (listen(sock_server, 0) != 0)
    {
        cout << "listen函数调用失败!" << WSAGetLastError() << endl;
        closesocket(sock_server);
        WSACleanup();
        return 0;
    }
    else
    {
        cout << "listenning......\n";
    }
    /*接收并处理客户连接*/
    FD_SET fd_read; //fd_set 里面放的是socket,是一个socket集合
    FD_ZERO(&fd_read);
    FD_SET(sock_server, &fd_read);
    FD_SET copy(fd_read);
    while (true) {
        const timeval tv = { 1,0 };
        fd_read = copy;
        //cout << fd_read.fd_count << endl;
        int ret = select(NULL, &fd_read, NULL, NULL, &tv);//select会识别有没有网络事件发送,若没有就阻塞在这里。加了个timeval代表让他等一秒就下执行
        int fdCount = fd_read.fd_count;
        for (int i = 0;i < fdCount;i++) {
            if (fd_read.fd_array[i] == sock_server) {
                sockaddr_in clientAddr;
                int nlen = sizeof(sockaddr_in);
                SOCKET sClient = accept(sock_server, (sockaddr*)&clientAddr, &nlen);
                /* 第一个参数sockfd为服务器的socket描述字,
                   第二个参数addr为指向struct sockaddr* 的指针,用于返回客户端的协议地址,
                   第三个参数addrlen为协议地址的长度。*/
                if (sClient == SOCKET_ERROR) {
                    cout << "接收客户端失败" << endl;
                    closesocket(sock_server);
                    return -1;
                }
                else cout << "connect client success" << endl;
                FD_SET(sClient, &fd_read);
                FD_SET(sClient, &copy);
            }
            else {
                char msg[1024];
                memset(msg, '\0', sizeof(msg));
                int res = recv(fd_read.fd_array[i], msg, 1000, 0);
                //for (auto j : copy.fd_array) {   //向其他客户端发送的消息
                //	if (j != sock_server && j != fd_read.fd_array[i]) {
                //		send(j, msg, 1000, 0);
                //	}
                //}
                /***********向data.txt文件写入数据*************/
                /*char* s;
                s = ((MsgTalk*)msg)->getBuff();
                writefile(s, hfile);*/

                /***********************************************/
                dealWithData((MsgHead *)msg,fd_read.fd_array[i]);
                if (res <= 0) {
                    FD_CLR(fd_read.fd_array[i], &copy);

                    cout << "quit" << endl;
                    break;
                }
            }
        }
    }
    closesocket(newsock);
    CloseHandle(hfile);
    closesocket(sock_server);
    WSACleanup();
    return 0;
}

void tcpsend(void* par)
{
    int size;
    SOCKET sock = (SOCKET)par;
    while (true)
    {
        char msg[1000];
        memset(msg, '\0', sizeof(msg));
        if ((size = recv(sock, msg,99, 0)) < 0)
        {
            cout << "接收信息失败,错误码:" << WSAGetLastError() << endl;
            break;
        }
        else if (size == 0)
        {
            break;
        }
        else   //输出收到的数据
        {
            //将数据中的整型数据字段的值由网络字节顺序转换为主机字节顺序
            //stud.examination = htonl(stud.examination);
            cout << "收到的消息:" << msg << endl;
        }
    }
}

int writefile(char* num,HANDLE fileHandle) {

    BOOL bRet = WriteFile(fileHandle, num, strlen(num), &d1, NULL);
    char c = '\n';

    if (!bRet) {
        int w = GetLastError();
        MessageBox(NULL, TEXT("数据写入失败"), TEXT("标头"), MB_OK);
        return w;
    }
    bRet = WriteFile(fileHandle, &c,1, &d1, NULL);
    return 0;
}





Client:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <iostream>
#include <thread>
#include<vector>
#include<queue>
#include<string>
#include<mutex>
#include<future>

#define PORT 65432
#define MSG_SHOW 1
#define MSG_CREATE 2
#define MSG_JOIN 3
#define MSG_LEAVE 4
#define MSG_QUIT 5
#define MSG_EXIT 5
#define MSG_TALK 6
#pragma comment(lib, "Ws2_32.lib")
using namespace std;

class MsgHead
{
public:
    int msgType;
    int dataLength;
};

class MsgShow : public MsgHead
{
public:
    MsgShow()
    {
        msgType = MSG_SHOW;
        dataLength = sizeof(MsgShow);
    }

};

class MsgCreate : public MsgHead
{
public:
    MsgCreate()
    {
        msgType = MSG_CREATE;
        dataLength = sizeof(MsgCreate);
    }

};

class MsgJoin : public MsgHead
{

public:
    MsgJoin(int number)
    {
        msgType = MSG_JOIN;
        dataLength = sizeof(MsgJoin);
        this->number = number;
    }

    int number;
};

class MsgLeave : public MsgHead
{
public:
    MsgLeave()
    {
        msgType = MSG_LEAVE;
        dataLength = sizeof(MsgLeave);
    }

};

class MsgExit : public MsgHead
{
public:
    MsgExit()
    {
        msgType = MSG_EXIT;
        dataLength = sizeof(MsgExit);
    }

};

class MsgTalk : public MsgHead
{
    char buff[1024];
public:
    MsgTalk()
    {
        memset(buff, '\0', sizeof(buff));
        msgType = MSG_TALK;
        dataLength = sizeof(MsgTalk);
    }

    char *getBuff()
    {
        return buff;
    }
};

LPVOID chat(LPVOID ptr)
{
    SOCKET sClient = *((SOCKET *) ptr);
    cout<<sClient<<endl;
    cout << "please input: show create join leave exit/quit" << endl;
    while (1)
    {
        char buff[1024];
        cin>>buff;  //会清掉最后输的换行符
        if (strcmp(buff, "show") == 0)
        {
            MsgShow msgShow;
            send(sClient, (const char *) &msgShow, msgShow.dataLength, 0);

            //显示聊天室
        } else if (strcmp(buff, "join") == 0)
        {
            cout << "input digital:" << endl;
            int number;
            cin >> number;
            MsgJoin msgJoin(number);
            send(sClient, (const char *) &msgJoin, msgJoin.dataLength, 0);
            //加入一个聊天室
        } else if (strcmp(buff, "create") == 0)
        {
            MsgCreate msgCreate;
            send(sClient, (const char *) &msgCreate, msgCreate.dataLength, 0);
            //创建一个聊天室
        } else if (strcmp(buff, "talk") == 0)
        {
            cout << "start talk" << endl;
            MsgTalk msgTalk;
            while (1)
            {

                cin >> msgTalk.getBuff();
                send(sClient, (const char *) &msgTalk, msgTalk.dataLength, 0);
            }
        }
    }
}

int main()
{
    /*定义相关变量*/
    SOCKET sock_client;                            //定义客户端套接字
    struct sockaddr_in server_addr;                //定义存放服务器端地址的结构变量
    int addr_len = sizeof(struct sockaddr_in);    //地址结构变量长度
    /*初始化*/
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        cout << "加载winsock.dll失败!\n";
        return 0;
    }
    /*创建套接字*/
    if ((sock_client = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        cout << "创建套接字失败!错误代码:" << WSAGetLastError() << endl;
        WSACleanup();
        return 0;
    }
    /*输入服务器IP地址以及填写服务器地址结构*/
    char IP[20];
    cout << "please input server ip:";
    cin >> IP;
    memset((void *) &server_addr, 0, addr_len);//地址结构清零
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    in_addr a;
    inet_pton(AF_INET, IP, &a);
    server_addr.sin_addr.s_addr = a.S_un.S_addr;//服务器IP地址
    /*Connecting Server*/
    SOCKET_ERROR;
    if (connect(sock_client, (struct sockaddr *) &server_addr, addr_len) != 0)
    {
        cout << "Connecting Error!!" << WSAGetLastError() << endl;
        closesocket(sock_client);
        WSACleanup();
        return 0;
    }
    
    future<HANDLE> futureResult = async(chat, &sock_client);

    // 等待线程结束

    while (1)
    {
        char buff[1000];

        int ret = recv(sock_client, buff, 1024, 0);
        if (ret > 0)
        {
            cout << ((MsgTalk *) buff)->getBuff() << endl;
        } else
        {
            cout << "failed ! " << endl;
        }

    }

    //HANDLE = futureResult.get();
    //WaitForSingleObject();
    closesocket(sock_client);
    WSACleanup();

    return 0;
}

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值