【linux网络编程】C++实现本地简易聊天室服务器(100行代码)

学习了多线程与Socket套接字后,打算写一个类似qq的本地群聊聊天室作为练习,于是就写了下面的这些代码。运行环境为linux,编程语言为c++11。

话不多说直接上代码。

服务器代码:

#include <iostream>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <queue>
#include <utility>

using namespace std;
#define SERV_PORT 5555                        // port number

queue<pair<pthread_t,string>>WriteQueue;      //this helps to contain the message which has been read and need to be send to other sockets.
vector<pair<pthread_t,int>>Thread;            //the second of the pair is the file describer of the socket.
pthread_mutex_t mutex;

void* WriteMessageOut(void *)
{
    while(1)
    {   
        while(!WriteQueue.empty())
        {
            pthread_mutex_lock(&mutex);
        
            pthread_t sender=WriteQueue.front().first;
            string s=WriteQueue.front().second;
            WriteQueue.pop();
            s="user "+to_string(sender%1000)+":"+s;
            cout<<s<<endl;

            for(auto pair:Thread)
            {
                if(sender!=pair.first)
                {
                    int fd=pair.second;
                    char str[BUFSIZ];
                    strcpy(str,s.c_str());
                    write(fd,str,strlen(str));
                }
            }
            pthread_mutex_unlock(&mutex);
        }
        usleep(1000);                       //sleep for 1000 microseconds.(weimiao)
    }
    return NULL;
}

void* ReadMessageIn(void *arg)
{
    int fd=*reinterpret_cast<int*>(arg);
    int ret;
    char buf[BUFSIZ];
    while((ret=read(fd, buf, BUFSIZ))>0)
    {
        buf[ret] = '\0';
        string s(buf);
        WriteQueue.emplace(pthread_self(),s);
    }
    close(fd);
    string s="user " +to_string(pthread_self() % 1000)+" exited.\n";
    cout << s << endl;
    WriteQueue.emplace(pthread_self(), s);
    pthread_exit(0);
    return NULL;
}

pthread_t CreateThread(void*(*f)(void *),void *arg)
{
    pthread_t ret;
    pthread_create(&ret,NULL,f,arg);
    //pthread_detach(ret);                //分离该线程,防止僵尸线程
    return ret;
}

int main()
{
    int sid;
    int ret;
    int fd;

    pthread_mutex_init(&mutex, NULL);

    sid=socket(AF_INET,SOCK_STREAM,0);
    
    struct sockaddr_in servaddr;
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(SERV_PORT);
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

    ret=bind(sid,(struct sockaddr*)&servaddr,sizeof(servaddr));

    listen(sid,128);

    pthread_t WriteIn=CreateThread(WriteMessageOut,NULL);

    cout<<"Server Started."<<endl;
    while((fd=accept(sid,NULL,NULL))!=-1)
    {
      pthread_t pid_thread =CreateThread(ReadMessageIn, (void *)&fd);
      Thread.emplace_back(pid_thread, fd);
      string s = "user " + to_string(pid_thread % 1000) + " connected.\n";
      cout << s<<endl;
      WriteQueue.emplace(pid_thread, s);
    }

    close(sid);
    pthread_mutex_destroy(&mutex);

    return 0;
}

代码运行截图:

代码使用方法:

        首先使用g++进行编译,接着启动服务器。由于该程序比较简单就没有设置客户端的代码,这里在linux下打开任意终端后,输入下述指令来连接本地服务器。

nc 127.0.0.1 5555

        nc是netcat的意思,后面的127.0.0.1是本地的虚拟地址,在后面的5555是服务器的端口号,这样就可以连上服务器。

        连上服务器后就可以在终端里打字说话,其他在服务器的用户可以收到你的信息。

       接着如果需要断开与服务器的连接,在控制台输入 ctrl+c 来终止程序,或者直接关闭终端,这样就可以断开服务器了。

代码分析:

        queue<pair<pthread_t,string>>WriteQueue;,用来存储待发送的消息队列,二元组pair的第一个参数用来储存发送该消息的线程id,第二个用来储存待发送的消息。该队列中的所有消息都会被发送给服务器中的其他用户。

        vector<pair<pthread_t,int>>Thread;,用来储存打开的线程与其对应分配的套接字的文件描述符。

        程序运行后,首先新建一个线程用于监听WriteQueue队列,该线程负责转发用户消息到其他用户。

        接着服务器调用accept函数等待连接,并为每个用户分配Socket套接字的文件描述符和一个线程来收发数据。

        服务器关闭,释放资源。

代码不足:

        Thread数组本来是设计用于服务器关闭时销毁资源的,该程序由于会一直在accept循环中无法出来,所以就没有显示销毁资源。线程没有得到显示的释放。

        对于临界区的访问:mutex锁应该有更好的用法,该程序的并发性不好,还没有测试过在并发访问情况下的性能如何。

        最后,使用英文注释是因为我没有打开搜狗输入法hhh,VMware切换输入法不是很舒服(应该是因为我还没有设置的原因)。

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值