聊天室功能:
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運行結果:
上面的測試只開啓了的三個客戶端程序,可開啓更多客戶端進行測試。
謝謝閱讀,有任何疑問可以留言。