多人实时聊天软件基础版(基于linux系统)
编程语言:c++
远程连接工具:vscode、xftp
服务器:阿里云服务器 Centos 8.2
效果图
服务器源码
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string>
#include<unistd.h>//linux下头文件
#include<string.h>
#include<netdb.h>//linux特有,与网络有关的结构体、宏、函数
#include<sys/types.h>//linux
#include<sys/socket.h>
#include<thread>
#include<vector>
#include<arpa/inet.h>//一些转换函数
#include<unordered_map>
using namespace std;
bool if_quit(string q,char *buffer);
void check_parameter(const int &argc);
void creat_sever_socket(int & sockfd);
void bind_socket(sockaddr_in &servaddr,short port,int &sockfd);
void begin_listen(int &sockfd);
void accept_client(vector<int> &all_client_socket, int &serverfd,unordered_map<int,char*> &clientfd_to_name);
bool recv_message(char *buffer,int buffer_size,int &clientfd,bool &if_get_name);
bool send_message(char *buffer,int buffer_size,const int &clientfd);
void print_message(char *buffer,int flag);
void close_socket(const vector<int> &sockfd);
void recv_and_send(int clientfd,vector<int> &all_client_socket,unordered_map<int,char*> &clientfd_to_name,bool &if_get_name);
int main(int argc, char *argv[]){
unordered_map<int, char *> clientfd_to_name;
check_parameter(argc);
int listenfd;
creat_sever_socket(listenfd);
struct sockaddr_in servaddr;
bind_socket(servaddr,htons(atoi(argv[1])),listenfd);
begin_listen(listenfd);//开始监听端口
vector<int> all_client_socket;//存储所有客户端套接字
accept_client(all_client_socket,listenfd,clientfd_to_name);//主线程,挂起等待客户端连接
getchar();
close(listenfd);
close_socket(all_client_socket);
return 0;
}
void check_parameter(const int &argc){
cout << "check_parameter starting..." << endl;
if(argc != 2){
cout << "参数个数错误!!!" << endl;
exit(0);
}
}
void creat_sever_socket(int & sockfd){
cout << "creat_sever_socket starting..." << endl;
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1){
cout <<"创建服务端socket错误" <<endl;
exit(0);
}
}
void bind_socket(sockaddr_in &servaddr,short port,int &sockfd){
cout << "bind_socket starting..." << endl;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = port;
int ans = bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(ans != 0){
cout << "bind 绑定错误!" << endl;
exit(0);
}
}
void begin_listen(int &sockfd){
cout << "begin_listen starting..." << endl;
int ans = listen(sockfd,5);
if(ans != 0){
cout << "listen 监听错误!"<<endl;
exit(0);
}
}
void accept_client(vector<int> &all_client_socket, int &serverfd,unordered_map<int,char*> &clientfd_to_name){
while(1){
bool if_get_name = false;
int clientfd;
struct sockaddr_in clientaddr;
int socklen = sizeof(struct sockaddr_in);
memset(&clientaddr,0,sizeof(clientaddr));
clientfd = accept(serverfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);
all_client_socket.push_back(clientfd);
cout << "****客户端 " << inet_ntoa(clientaddr.sin_addr) << " 已连接****"<<endl;
cout << "****socket :"<< clientfd << " ****" << endl;
thread t(recv_and_send,clientfd,std::ref(all_client_socket),std::ref(clientfd_to_name),std::ref(if_get_name));
t.detach();
}
}
bool recv_message(char *buffer,int buffer_size,int &clientfd,bool &if_get_name){
memset(buffer,0,buffer_size);
int ans = recv(clientfd,buffer,buffer_size,0);
return ans > 0 ? true : false;
}
bool send_message(char *buffer,int buffer_size,int &clientfd){
int ans = send(clientfd,buffer,buffer_size,0);
return ans > 0 ? true : false;
}
void recv_and_send(int clientfd,vector<int> &all_client_socket,unordered_map<int,char*> &clientfd_to_name,bool &if_get_name){
char buffer[1024];
char send_buffer[2048];
while(1){
if(if_get_name == false){
if_get_name = true;
recv_message(buffer,sizeof(buffer),clientfd,if_get_name);
char *name = new char[1024];
memcpy(name,buffer,sizeof(buffer));
clientfd_to_name[clientfd] = name;
char ok[] = "------昵称创建完成!------";
send_message(ok,sizeof(ok),clientfd);
continue;
}
if(recv_message(buffer,sizeof(buffer),clientfd,if_get_name) == false){
cout <<clientfd_to_name[clientfd] << "(clientfd : " << clientfd << ")"<< "退出,传输结束" <<endl;
break;
}
memset(send_buffer,0,sizeof(send_buffer));
char tmp[] = " : ";
strcat(send_buffer,clientfd_to_name[clientfd]);
strcat(send_buffer,tmp);
strcat(send_buffer,buffer);
for(auto i : all_client_socket){
if(i != clientfd){
cout << "已向 " << clientfd_to_name[i] << "(clientfd : " << i << ")"<< " 发送 " <<buffer << endl;
send_message(send_buffer,sizeof(send_buffer),i);
}
}
memset(buffer,0,sizeof(buffer));
cout <<"-------------"<<endl;
}
}
void close_socket(const vector<int> &sockfd){
for(auto i : sockfd){
close(i);
}
}
bool if_quit(string q,char *buffer){
int i = 0;
while(buffer[i] && q[i]){
if(buffer[i] != q[i]){
return false;
}
i++;
}
if(buffer[i] == 0 && q[i] == 0){
return true;
}
return false;
}
–客户端源码–
#include<iostream>
#include<stdio.h>
#include<thread>
#include<stdlib.h>
#include<unistd.h>//linux下头文件
#include<string.h>
#include<netdb.h>//linux特有,与网络有关的结构体、宏、函数
#include<sys/types.h>//linux
#include<sys/socket.h>
#include<arpa/inet.h>//一些转换函数
using namespace std;
void recv_message(int &sockfd);
void check_parameter(int argc);
void creat_client_socket(int &sockfd);
void get_server_ip(struct hostent* &h,char *ip,int &sockfd);
void connect_server(struct sockaddr_in &servaddr,char *port, int &sockfd,struct hostent * &h);
bool send_message(int &sockfd,char *buffer,int buffer_length);
bool if_quit(char *buffer);
int main(int argc,char *argv[]){
check_parameter(argc);
int sockfd;
creat_client_socket(sockfd);
struct hostent* h;
get_server_ip(h,argv[1],sockfd);
struct sockaddr_in servaddr;
connect_server(servaddr,argv[2],sockfd,h);
char buffer[1024];
cout << "输入quit退出" << endl;
cout << "------请创建昵称------" <<endl;
thread t(recv_message,std::ref(sockfd));
t.detach();
while(1)
{
if(send_message(sockfd,buffer,sizeof(buffer)) == false){
break;
}
}
close(sockfd);
}
void recv_message(int &sockfd){
char buffer[2048];
int buffer_size = 2048;
while(1){
memset(buffer,0,buffer_size);
if(recv(sockfd,buffer,buffer_size,0) <= 0){
cout << "接收结束" <<endl;
close(sockfd);
exit(0);
}
cout << buffer << endl;
}
}
void check_parameter(int argc){
if(argc != 3){
cout << "参数个数错误!"<<endl;
exit(0);
}
}
void creat_client_socket(int &sockfd){
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1){
cout <<"sock 创建错误!" <<endl;
exit(0);
}
}
void get_server_ip(struct hostent * &h,char *ip,int &sockfd){
h = gethostbyname(ip);
if(h == 0){
cout << "查找服务器ip错误!!" <<endl;
close(sockfd);
exit(0);
}
}
void connect_server(struct sockaddr_in &servaddr,char *port, int &sockfd,struct hostent * &h){
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(port));
memcpy(&servaddr.sin_addr, h->h_addr,h->h_length);
int ans = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(ans != 0 ){
cout << "connet 连接错误!" <<endl;
close(sockfd);
exit(0);
}
}
bool send_message(int &sockfd,char *buffer,int buffer_length){
int iret;
memset(buffer,0,buffer_length);
scanf("%[^\n]",buffer);
getchar();
if(if_quit(buffer)) return false;
iret = send(sockfd,buffer,buffer_length,0);
if(iret <= 0){
cout << "send 发送错误!" << endl;
return false;
}
return true;
}
bool if_quit(char *buffer){
char q[] = "quit";
int i = 0 ;
while(q[i] && buffer[i]){
if(q[i] != buffer[i]){
return false;
}
i++;
}
if(q[i] == 0 && buffer[i] == 0){
return true;
}
return false;
}
编译执行
//服务端使用教程:(长期运行)
//编译
[root@iZ2zehwjh31jv7wmif8bvyZ cpp]# g++ myserver.cpp -lpthread -o myserver
//运行
[root@iZ2zehwjh31jv7wmif8bvyZ cpp]# ./myserver 5005
//客户端使用教程:
//编译 :
[root@iZ2zehwjh31jv7wmif8bvyZ cpp]# g++ myclient.cpp -lpthread -o myclient
//运行
[root@iZ2zehwjh31jv7wmif8bvyZ cpp]# ./myclient xxx.xxx.xxx.xxx 5005
//(如果在其他服务器使用,注意修改ip和端口号)
*需要改进的地方
1.程序中每检测到一个client连接,就会创建一个新的线程维护此连接,这样会十分占用内存,考虑改成少量线程+epoll,每个线程做一件事,节约内存
2.recv函数接收的数据报如果太大的话,很有可能被tcp分段传输,这样就不能保证数据的完整性,考虑加入生产者消费者模型,用一个队列存储,数据完整后取出