聊天室应用后台实现:基于TCP连接的多进程并发服务器

聊天室功能:
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运行结果:
在这里插入图片描述

上面的测试只开启了的三个客户端程序,可开启更多客户端进行测试。

谢谢阅读,有任何疑问可以留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章