├── Client.cpp ├── Client.h ├── ClientMain.cpp ├── Common.h ├── Makefile ├── README.md ├── Server.cpp ├── Server.h └── ServerMain.cpp /Client.cpp: -------------------------------------------------------------------------------- 1 | #include"Client.h" 2 | 3 | using namespace std; 4 | 5 | //客户端类成员函数 6 | //客户端类构造函数 7 | Client::Client() 8 | { 9 | //初始化要连接的服务器地址和端口 10 | serverAddr.sin_family = AF_INET; 11 | serverAddr.sin_port = htons(SERVER_PORT); 12 | inet_pton(AF_INET,SERVER_IP,&serverAddr.sin_addr.s_addr); 13 | 14 | //初始化socket 15 | sock = 0; 16 | 17 | //初始化进程号 18 | pid = 0; 19 | 20 | //客户端状态 21 | isClientwork = true; 22 | 23 | //epoll fd 24 | epfd = 0; 25 | } 26 | 27 | //连接服务器 28 | void Client::Connect() 29 | { 30 | cout<<"Connect Server: "<0 父进程 136 | //父进程负责读管道数据,因此先关闭写端 137 | close(pipe_fd[1]); 138 | 139 | //主循环epoll_wait 140 | while(isClientwork) 141 | { 142 | int epoll_events_count = epoll_wait(epfd,events,2,-1); 143 | 144 | //处理就绪事件 145 | for(int i = 0;i 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | //默认服务器端IP地址 16 | #define SERVER_IP "127.0.0.1" 17 | 18 | //服务器端口号 19 | #define SERVER_PORT 8888 20 | 21 | //epoll支持的最大句柄数(建议数) 22 | #define EPOLL_SIZE 5000 23 | 24 | //缓冲区大小(65535) 25 | #define BUF_SIZE 0XFFFF 26 | 27 | //新用户登录后的欢迎信息 28 | #define SERVER_WELCOME "Welcome you join to the char room!Your chat ID is: Client %d" 29 | 30 | //其他用户收到消息的前缀 31 | #define SERVER_MESSAGE "ClientID %d say >> %s" 32 | #define SERVER_PRIVATE_MESSAGE "Client %d say to you privately >> %s" 33 | #define SERVER_PRIVATE_ERROR_MESSAGE "Client %d is not in the chat room yet~" 34 | 35 | //退出系统 36 | #define EXIT "EXIT" 37 | 38 | //提醒你是聊天室中唯一的客户 39 | #define CAUTION "There is only one in the chat room!" 40 | 41 | //注册新的fd到epollfd中 42 | //参数enable_et表示是否启用ET模式,如果为true则启用,否则使用LT模式 43 | static void addfd(int epollfd,int fd,bool enable_et) 44 | { 45 | struct epoll_event ev; 46 | ev.data.fd = fd; 47 | ev.events = EPOLLIN; 48 | if(enable_et) 49 | ev.events = EPOLLIN|EPOLLET; 50 | epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev); 51 | 52 | //设置socket为非阻塞模式(套接字立即返回,不管I/O是否完成,该函数所在的线程会继续运行) 53 | fcntl(fd,F_SETFL,fcntl(fd,F_GETFD,0)|O_NONBLOCK); 54 | printf("fd added to epoll!\n\n"); 55 | } 56 | 57 | //定义信息结构,在服务端和客户端之间传送 58 | struct Msg 59 | { 60 | int type; //消息类别 61 | int fromID; //发送方FD 62 | int toID; //接收方FD 63 | char content[BUF_SIZE]; //文字信息 64 | }; 65 | #endif 66 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | 3 | all:ClientMain.cpp ServerMain.cpp Server.o Client.o 4 | $(CXX) ServerMain.cpp Server.o -o chatroom_server 5 | $(CXX) ClientMain.cpp Client.o -o chatroom_client 6 | Server.o:Server.cpp Server.h Common.h 7 | $(CXX) -c Server.cpp 8 | Client.o:Client.cpp Client.h Common.h 9 | $(CXX) -c Client.cpp 10 | 11 | clean: 12 | rm -f *.o chatroom_server chatroom_client 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Instant_messaging_system 2 | Linux C++ socket网络编程----------即时通信系统 3 | 4 | 项目内容: 5 | 本项目使用C ++实现一个具备服务器端和客户端即时通信且具有私聊功能的聊天室 6 | 目的是学习C++网络开发的基本概念,同时也熟悉了Linux下的C++程序编译和简单MakeFile的编写 7 | 需求分析: 8 | 1.服务端:能够接受新的客户连接,并将每个客户端发来的信息,广播给对应的目标客户端 9 | 2.客户端:能够连接服务器,并向服务器发送消息,同时可以接受服务器发来的消息 10 | 服务端: 11 | 1.支持多个客户端接入,实现聊天室基本功能 12 | 2.启动服务,建立监听端口等待客户端连接 13 | 3.使用epoll机制实现并发,增加效率 14 | 4.客户端连接时,发送欢迎消息,并存储连接记录 15 | 5.客户端发送消息时,根据消息类型,广播给所有用户(群聊)或者指定用户(私聊) 16 | 6.客户端请求退出时,对相应连接信息进行清理 17 | 客户端: 18 | 1.连接服务器 19 | 2.支持用户输入消息,发送给服务端 20 | 3.接受并显示服务端发来的消息 21 | 4.退出连接 22 | 客户端需要两个进程分别支持以下功能: 23 | 子进程: 24 | 1.等待用户输入信息 25 | 2.将聊天信息写入管道(pipe),并发送给父进程 26 | 父进程: 27 | 1.使用epoll机制接收服务端发来的消息,并显示给用户,使用户看到其他用户的信息 28 | 2.将子进程发送的聊天信息从管道(pipe)中读取出来,并发送给客户端 29 | -------------------------------------------------------------------------------- /Server.cpp: -------------------------------------------------------------------------------- 1 | #include"Server.h" 2 | 3 | using namespace std; 4 | 5 | //服务器端类成员函数 6 | //服务器端类构造函数 7 | Server::Server() 8 | { 9 | //初始化服务器地址和端口 10 | serverAddr.sin_family = AF_INET; 11 | serverAddr.sin_port = htons(SERVER_PORT); 12 | inet_pton(AF_INET,SERVER_IP,&serverAddr.sin_addr.s_addr); 13 | 14 | //初始化socket 15 | listener = 0; 16 | 17 | //初始化epfd 18 | epfd = 0; 19 | } 20 | 21 | //初始化服务端并启动监听 22 | void Server::Init() 23 | { 24 | cout<<"Init Server..."<::iterator it; 137 | for(it = clients_list.begin();it!=clients_list.end();it++) 138 | { 139 | if(*it != clientfd) 140 | { 141 | //把发送的结构体转换为字符串 142 | bzero(send_buf,BUF_SIZE); 143 | memcpy(send_buf,&msg,sizeof(msg)); 144 | if(send(*it,send_buf,sizeof(send_buf),0)<0) 145 | return -1; 146 | } 147 | 148 | } 149 | } 150 | 151 | //私聊 152 | if(msg.type == 1) 153 | { 154 | bool private_offline = true; 155 | sprintf(format_message,SERVER_PRIVATE_MESSAGE,clientfd,msg.content); 156 | memcpy(msg.content,format_message,BUF_SIZE); 157 | 158 | //遍历客户端列表发送对应消息,需要判断不要给来源客户端发 159 | list::iterator it; 160 | for(it = clients_list.begin();it!=clients_list.end();it++) 161 | { 162 | if(*it == msg.toID) 163 | { 164 | private_offline = false; 165 | //把发送的结构体转换为字符串 166 | bzero(send_buf,BUF_SIZE); 167 | memcpy(send_buf,&msg,sizeof(msg)); 168 | if(send(*it,send_buf,sizeof(send_buf),0)<0) 169 | return -1; 170 | } 171 | } 172 | 173 | //如果私聊对象不在线 174 | if(private_offline) 175 | { 176 | sprintf(format_message,SERVER_PRIVATE_ERROR_MESSAGE,msg.toID); 177 | memcpy(msg.content,format_message,BUF_SIZE); 178 | bzero(send_buf,BUF_SIZE); 179 | memcpy(send_buf,&msg,sizeof(msg)); 180 | if(send(msg.fromID,send_buf,sizeof(send_buf),0)<0) 181 | return -1; 182 | } 183 | } 184 | } 185 | return len; 186 | } 187 | 188 | //启动服务端 189 | void Server::Start() 190 | { 191 | //epoll 事件队列 192 | static struct epoll_event events[EPOLL_SIZE]; 193 | 194 | //初始化服务端 195 | Init(); 196 | 197 | //主循环 198 | while(1) 199 | { 200 | //epoll_events_count表示就绪事件的数目 201 | int epoll_events_count = epoll_wait(epfd,events,EPOLL_SIZE,-1); 202 | if(epoll_events_count<0) 203 | { 204 | perror("epoll_wait error: "); 205 | break; 206 | } 207 | 208 | cout<<"epoll_events_count = "< clients_list; //客户端列表 28 | }; 29 | #endif 30 | -------------------------------------------------------------------------------- /ServerMain.cpp: -------------------------------------------------------------------------------- 1 | #include"Server.h" 2 | 3 | //服务端主函数 4 | //创建服务端对象后启动服务端 5 | 6 | int main() 7 | { 8 | Server server; 9 | server.Start(); 10 | return 0; 11 | } 12 | --------------------------------------------------------------------------------