聊天室功能:
1、响应多个客户端连接请求
2、服务端及客户端信息的交互由用户指定输入
3、服务端采用多进程处理,每一个客户端连接,都有单独的进程去处理信息的接受与发送
服务端实现:
1、采用TCP socket建立通信连接
2、采用多进程方式处理多个客户端的连接请求
3、采用信号处理子进程退出后的资源回收问题,防止出现僵尸进程
4、采用write()、read()系统接口进行信息读写
服务端代码实现 chat_serve.cpp:
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
using namespace std;
/*
接收到子进程退出的信号后,调用waitpid()函数,获取子进程的结束状态值
避免操作系统一直保存子进程信息,进而出现僵尸进程,消耗系统资源。
*/
void childprocess(int sig)
{
if(sig == SIGCHLD)
{
int status;
pid_t pid = waitpid(-1, &status, WNOHANG);
if(WIFEXITED(status))
{
cout << "child process end. move it. child pid = " << pid;
cout << " and child return value = " << WEXITSTATUS(status) << endl;
}
}
}
int main(int argc, char* argv[])
{
// 创建socket()
int serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock == -1)
{
cout << "socket error" << endl;
exit(1);
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; // IPv4地址族
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
// 地址信息绑定
int bind_res = bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if(bind_res == -1)
{
cout << "bind error" << endl;
exit(1);
}
int listen_res = listen(serv_sock, 5);
if(listen_res == -1)
{
cout << "listen error" << endl;
exit(1);
}
// 信号注册: 在子进程结束后,将进程信息返回给父进程,防止出现僵尸进程
struct sigaction sigact;
sigact.sa_flags = 0;
sigact.sa_handler = childprocess;
sigemptyset(&sigact.sa_mask);
sigaction(SIGCHLD, &sigact, 0);
int child_count = 0;
int clnt_sock;
socklen_t clnt_adr_sz;
struct sockaddr_in clnt_addr;
while(1)
{
clnt_adr_sz = sizeof(clnt_addr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_adr_sz);
if(clnt_sock != -1)
{
cout << "client connect!!!" << endl;
++child_count;
}
pid_t pid = fork();
if(pid == 0 && clnt_sock != -1)
{
cout << "it's child process, num = " << child_count << endl;
close(serv_sock); // 关闭无用的套接字,只在父进程中使用
while(1)
{
char message[1024] = { 0 };
int read_len = read(clnt_sock, message, sizeof(message) - 1);
message[read_len] = 0;
cout << "<child-" << child_count << ">: " << message << endl;
string serv_msg;
if(message[0] == 'q' || message[0] == 'Q')
{
serv_msg = "byby, client.";
}
else
{
cout << "<child-" << child_count << ">: " << "please input msg send to client: ";
getline(cin, serv_msg);
}
write(clnt_sock, serv_msg.c_str(), serv_msg.length());
if(message[0] == 'q' || message[0] == 'Q')
{
cout << "child process end. child num = " << child_count << endl;
return 1;
}
}
}
else
{
close(clnt_sock); // 关闭无用的套接字,只在子进程中使用
}
}
close(serv_sock);
return 0;
}
客户端实现:
1、采用TCP socket建立通信连接
2、采用write()、read()系统接口进行信息读写
客户端代码实现 chat_client.cpp :
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
#define BUF_SIZE 100
int main(int argc, char* argv[])
{
char message[BUF_SIZE] = { 0 };
int sock = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
if(connect(sock,(struct sockaddr*)&serv_addr, sizeof(serv_addr)) != -1)
{
cout << "connected success..." << endl;
}
while(1)
{
cout << "input msg you want send to server:";
string msg;
getline(cin, msg);
write(sock, msg.c_str(), msg.length());
if(msg.compare("q") == 0 || msg.compare("Q") == 0)
{
cout << "client end, byby." << endl;
break;
}
char msg2[1000];
int read_len = read(sock, msg2, sizeof(msg2) - 1);
msg2[read_len] = 0;
cout << "server:" << msg2 << endl;
}
close(sock);
return 0;
}
代码在Linux下使用g++进行编译:
运行一个服务端程序,三个客户端程序,并进行信息交换:
(1)服务端运行结果:
(2)客户端1运行结果:
(3)客户端2运行结果:
(4)客户端3运行结果:
上面的测试只开启了的三个客户端程序,可开启更多客户端进行测试。
谢谢阅读,有任何疑问可以留言。