首页建站网络聊天系统(基于Linux系统的网络聊天室实现方案)

网络聊天系统(基于Linux系统的网络聊天室实现方案)

编程之家2024-02-05107次浏览

一、linux多人聊天室是怎么搭建的

Linux多人聊天室的搭建主要依赖一个n次多路转发器(MUX)程序,它需要网络编程语言,比如C和JavaScript等来实现。这个程序能够将多个客户端连接,并转发消息。另外,还需要一个服务器,用来控制不同用户的访问权限。另外,还需要一个客户端的GUI程序,用来显示聊天窗口、聊天室列表等。最后,还需要一个数据库来持久化存储用户的聊天信息。

网络聊天系统(基于Linux系统的网络聊天室实现方案)

二、linux多人聊天室是如何实现的

多人聊天室可通过保持网络连接、保持进程运行保持通信。想要自己搭建多人聊天室其实也很简单,你可以直接用ZEGO即时通讯,内置文本、图片、语音、视频、地理位置等各种消息类型,支持单聊、群聊、房间聊天,自由组合IM能力,也可针对自身业务场景,定制所需服务规格。

三、如何编写linux聊天室

自从开始学linux网络编程后就想写个聊天室,一开始原本打算用多进程的方式来写,可是发觉进程间的通信有点麻烦,而且开销也大,后来想用多线程能不能实现呢,于是便去看了一下linux里线程的用法,实际上只需要知道 pthread_create就差不多了,于是动手开干,用了两天时间,调试的过程挺痛苦的,一开始打算用纯C来撸,便用简单的数组来存储客户端的连接信息,可是运行时出现了一些很奇怪的问题,不知道是不是访问了临界资源,和线程间的互斥有关等等;奇怪的是,当改用STL的set或map时问题就解决了,但上网搜了下发现STL也不是线程安全的,至于到底是什么问题暂时不想去纠结了,可能是其它一些小细节的错误吧。先贴上代码:

首先是必要的头文件 header.h:

#ifndef __HEADER_H#define __HEADER_H#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<error.h>#include<signal.h>#include<sys/wait.h>#include<assert.h>#include<pthread.h>#define bool int// the 3 lines is for c originally#define true 1#define false 0#define PORT 9003#define BUF_LEN 1024//缓冲区大小#define MAX_CONNECTION 6//服务器允许的最大连接数,可自行更改#define For(i,s,t) for(i=(s); i!=(t);++i)#endif// __HEADER_H

然后是客户端部分client.cpp,相对来说简单一些:

#include"header.h"//客户端接收消息的线程函数void* recv_func(void*args)

网络聊天系统(基于Linux系统的网络聊天室实现方案)

{ char buf[BUF_LEN]; int sock_fd=*(int*)args; while(true){ int n= recv(sock_fd, buf, BUF_LEN, 0); if(n<= 0) break;//这句很关键,一开始不知道可以用这个来判断通信是否结束,用了其它一些很奇葩的做法来结束并关闭 sock_fd以避免 CLOSE_WAIT和 FIN_WAIT2状态的出现T.T write(STDOUT_FILENO, buf, n);

}

close(sock_fd);

exit(0);

}//客户端和服务端进行通信的处理函数void process(int sock_fd)

{

网络聊天系统(基于Linux系统的网络聊天室实现方案)

pthread_t td;

pthread_create(&td, NULL, recv_func,(void*)&sock_fd);//新开个线程来接收消息,避免了一读一写的原始模式,一开始竟把它放进 while循环里面了,泪崩。。。

char buf[BUF_LEN]; while(true){ int n= read(STDIN_FILENO, buf, BUF_LEN);

buf[n++]='\0';//貌似标准读入不会有字符串结束符的,需要自己手动添加

send(sock_fd, buf, n, 0);

}

close(sock_fd);

}int main(int argc, char*argv[])

{

assert(argc== 2); struct sockaddr_in cli;

bzero(&cli, sizeof(cli));

cli.sin_family= AF_INET;

cli.sin_addr.s_addr= htonl(INADDR_ANY);

cli.sin_port= htons(PORT);//少了 htons的话就连接不上了,因为小端机器的原因???

int sc= socket(AF_INET, SOCK_STREAM, 0); if(sc< 0){

perror("socket error");

exit(-1);

}

inet_pton(AF_INET, argv[1],&(cli.sin_addr));//用第一个参数作为连接服务器端的地址

int err= connect(sc,(struct sockaddr*)&cli, sizeof(cli)); if(err< 0){

perror("connect error");

exit(-2);

}

process(sc);

close(sc); return 0;

}

最后是服务端 server.cpp:

#include<map>#include"header.h"using std::map;

map<int, struct sockaddr_in*> socks;//用于记录各个客户端,键是与客户端通信 socket的文件描述符,值是对应的客户端的 sockaddr_in的信息//群发消息给 socks中的所有客户端inline void send_all(const char*buf, int len)

{ for(auto it= socks.begin(); it!= socks.end();++it)

send(it->first, buf, len, 0);

}//服务端端接收消息的线程函数void* recv_func(void* args)

{ int cfd=*(int*)args; char buf[BUF_LEN]; while(true){ int n= recv(cfd, buf, BUF_LEN, 0); if(n<= 0) break;//关键的一句,用于作为结束通信的判断 write(STDOUT_FILENO, buf, n); if(strcmp(buf,"bye\n")== 0){//如果接收到客户端的 bye,就结束通信并从 socks中删除相应的文件描述符,动态申请的空间也应在删除前释放

printf("close connection with client%d.\n", cfd); free(socks[cfd]);

socks.erase(cfd); break;

}

send_all(buf, n);//群发消息给所有已连接的客户端}

close(cfd);//关闭与这个客户端通信的文件描述符}//和某一个客户端通信的线程函数void* process(void*argv)

{

pthread_t td;

pthread_create(&td, NULL, recv_func,(void*)argv);//在主处理函数中再新开一个线程用于接收该客户端的消息

int sc=*(int*)argv; char buf[BUF_LEN]; while(true){ int n= read(STDIN_FILENO, buf, BUF_LEN);

buf[n++]='\0';//和客户端一样需要自己手动添加字符串结束符

send_all(buf, n);//服务端自己的信息输入需要发给所有客户端}

close(sc);

}int main(int argc, char*argv[])

{ struct sockaddr_in serv;

bzero(&serv, sizeof(serv));

serv.sin_family= AF_INET;

serv.sin_addr.s_addr= htonl(INADDR_ANY);

serv.sin_port= htons(PORT); int ss= socket(AF_INET, SOCK_STREAM, 0); if(ss< 0){

perror("socket error"); return 1;

} int err= bind(ss,(struct sockaddr*)&serv, sizeof(serv)); if(err< 0){

perror("bind error"); return 2;

}

err= listen(ss, 2); if(err< 0){

perror("listen error"); return 3;

}

socks.clear();//清空 map

socklen_t len= sizeof(struct sockaddr); while(true){ struct sockaddr_in*cli_addr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); int sc= accept(ss,(struct sockaddr*)cli_addr,&len); if(sc< 0){ free(cli_addr); continue;

} if(socks.size()>= MAX_CONNECTION){//当将要超过最大连接数时,就让那个客户端先等一下

char buf[128]="connections is too much, please waiting...\n";

send(sc, buf, strlen(buf)+ 1, 0);

close(sc); free(cli_addr); continue;

}

socks[sc]= cli_addr;//指向对应申请到的 sockaddr_in空间

printf("client%d connect me...\n", sc);

pthread_t td;

pthread_create(&td, NULL, process,(void*)&sc);//开一个线程来和 accept的客户端进行交互} return 0;

}

makefile文件:

all: server client

server: server.cpp

g++-std=c++11-o server server.cpp-lpthread

client: client.cpp

g++-std=c++11-o client client.cpp-lpthread

clean:

rm-f*.o

在我的ubuntu 14.04 64位的机器上测试过没有什么问题,客户端与服务端能正常的交互和退出,能通过服务端接收其它客户端发送的消息,运行时cpu和内存占用情况正常,不会产生什么奇怪的bug。暂时只写了个终端的界面,客户端的UI迟点再去弄吧~

*****************************************************************************************************************************************

今天试了下用 PyQt4去写个客户端的界面,调了好一天,总算能看到点东西了,先上图:

而命令行下的客户端(上面的 client.cpp文件)的运行界面是这样子的:

服务端的运行情况是:

PyQt4编写的客户端(pyqt_client.py)代码是:

#!/usr/bin/env python#-*- coding: utf-8-*-from PyQt4 import QtGui, QtCoreimport sysimport socketimport threadclass Client(QtGui.QWidget):

BUF_LEN= 1024 def __init__(self, parent=None):

QtGui.QWidget.__init__(self, parent)

self.setWindowTitle(u'TCP客户端')

self.resize(600, 500)

self.center()

layout= QtGui.QGridLayout(self)

label_ip= QtGui.QLabel(u'远程主机IP:')

layout.addWidget(label_ip, 0, 0, 1, 1)

self.txt_ip= QtGui.QLineEdit('127.0.0.1')

layout.addWidget(self.txt_ip, 0, 1, 1, 3)

label_port= QtGui.QLabel(u'端口:')

layout.addWidget(label_port, 0, 4, 1, 1)

self.txt_port= QtGui.QLineEdit('9003')

layout.addWidget(self.txt_port, 0, 5, 1, 3)

self.isConnected= False

self.btn_connect= QtGui.QPushButton(u'连接')

self.connect(self.btn_connect, QtCore.SIGNAL('clicked()'), self.myConnect)

layout.addWidget(self.btn_connect, 0, 8, 1, 2)

label_recvMessage= QtGui.QLabel(u'消息内容:')

layout.addWidget(label_recvMessage, 1, 0, 1, 1)

self.btn_clearRecvMessage= QtGui.QPushButton(u'↓清空消息框')

self.connect(self.btn_clearRecvMessage, QtCore.SIGNAL('clicked()'), self.myClearRecvMessage)

layout.addWidget(self.btn_clearRecvMessage, 1, 7, 1, 3)

self.txt_recvMessage= QtGui.QTextEdit()

self.txt_recvMessage.setReadOnly(True)

self.txt_recvMessage.setStyleSheet('background-color:yellow')

layout.addWidget(self.txt_recvMessage, 2, 0, 1, 10)

lable_name= QtGui.QLabel(u'姓名(ID):')

layout.addWidget(lable_name, 3, 0, 1, 1)

self.txt_name= QtGui.QLineEdit()

layout.addWidget(self.txt_name, 3, 1, 1, 3)

self.isSendName= QtGui.QRadioButton(u'发送姓名')

self.isSendName.setChecked(False)

layout.addWidget(self.isSendName, 3, 4, 1, 1)

label_sendMessage= QtGui.QLabel(u'输入框:')

layout.addWidget(label_sendMessage, 4, 0, 1, 1)

self.txt_sendMessage= QtGui.QLineEdit()

self.txt_sendMessage.setStyleSheet("background-color:cyan")

layout.addWidget(self.txt_sendMessage, 4, 1, 1, 7)

self.btn_send= QtGui.QPushButton(u'发送')

self.connect(self.btn_send, QtCore.SIGNAL('clicked()'), self.mySend)

layout.addWidget(self.btn_send, 4, 8, 1, 2)

self.btn_clearSendMessage= QtGui.QPushButton(u'↑清空输入框')

self.connect(self.btn_clearSendMessage, QtCore.SIGNAL('clicked()'), self.myClearSendMessage)

layout.addWidget(self.btn_clearSendMessage, 5, 6, 1, 2)

self.btn_quit= QtGui.QPushButton(u'退出')

self.connect(self.btn_quit, QtCore.

scalemode(scalemode属性 html)psp记忆棒格式化(psp记忆棒试软件)