Linux网络编程之多进程并发服务器(其二)


本博客参考:《Linux C/C++ 服务器开发实践》——朱文伟 李建英

多进程并发服务器是什么?

多进程服务器是指当客户端有请求时,服务器用一个子进程来处理客户端的请求,父进程继续等待其他客户端的请求。这种方法的优点是当客户端有请求时,服务器能及时处理,特别是在客户端服务器交互系统中。对于一个TCP服务器,客户端与服务器的连接可能不会马上关闭,而会等到客户端提交某些数据后再关闭,这段时间服务器的进程会阻塞,所以这时操作系统可能会调用其他客户端服务进程,这比起循环服务器大大提高了服务性能。

TCP多进程并发服务器的编程模型

该服务器的编程模型涉及到fork函数的使用,具体可以看这篇文章:
链接:Linux之进程(详解-上)
其编程模型的伪代码如下:

#include <头文件>
int main(){
	创建套接字sockfd
	绑定(bind)套接字sockfd
	监听(listen)套接字sockfd

	while(1){
	int connfd = accept();
	if(fork() == 0){			//	子进程
		close(sockfd);			//关闭监听套接字
		fun();					//服务客户端的具体事件在fun里实现
		close(connfd);			//关闭已连接套接字connfd
		exit(0);				//结束子进程
	}
	close(connfd);				//关闭已连接套接字connfd
	}

	close(sockfd);
	return 0;
}

在子进程中,先要关闭一次监听套接字,因为监听套接字属于内核对象,创建子进程的时候,会导致操作系统底层对该内核对象的引用计数加1,导致该描述符的引用计数为2,要想它变成0来关闭监听描述符,需在子进程中关闭一次,让引用计数变成1,然后在父进程中再关闭一次,就会变成0。

一个简单的多进程TCP服务器

tcpserver.cpp

#include "myhead.h"
#define PORT 10024
int main(){
    char on = 1;
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        perror("create sockfd fail\n");
        return -1;
    }
    //配置本地网络信息
    struct sockaddr_in seraddr;
    memset(&seraddr, 0, sizeof(seraddr));
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(PORT);
    seraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    //设置端口复用
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    int err_log = bind(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr));
    if(err_log < 0){
        perror("bind fail\n");
        return -1;
    }

    err_log = listen(sockfd, 10);
    if(err_log < 0){
        perror("listen fail\n");
        return -1;
    }

    while(1){
        char cli_ip[INET_ADDRSTRLEN] = {0};
        struct sockaddr_in clientAddr;
        socklen_t cliaddr_len = sizeof(clientAddr);
        puts("Father process is waitting client...\n");
        //等待客户端连接
        int connfd = accept(sockfd, (struct sockaddr*)&clientAddr, &cliaddr_len);
        if(connfd < 0){
            perror("accept fail \n");
            close(sockfd);
            return -1;
        }

        pid_t pid = fork();
        if(pid < 0){
            perror("fork fail\n");
            return -1;
        }
        else if(pid == 0){     //子进程接收客户端的信息,并返回给客户端
            close(sockfd);     //关闭监听套接字,这个套接字是从父进程继承过来的
            char recv_buf[1024] = {0};
            int recv_len = 0;
            // 打印客户端的IP地址和端口号
            memset(cli_ip, 0, sizeof(cli_ip));
            inet_ntop(AF_INET, &clientAddr.sin_addr, cli_ip, INET_ADDRSTRLEN);
            printf("-------------------------\n");
            printf("client ip = %s, port = %d\n", cli_ip, ntohs(clientAddr.sin_port));
            //循环接收数据
            while((recv_len = recv(connfd, recv_buf, sizeof(recv_buf), 0)) > 0){
                printf("recv_buf: %s\n", recv_buf);
                send(connfd, recv_buf, recv_len, 0);

            }
            printf("client_port %d closed \n", ntohs(clientAddr.sin_port));
            close(connfd);
            return -1;
        }
        else if(pid > 0){
            close(connfd);
        }
    }

    close(sockfd);
    return 0;
}

tcpclient.cpp

 
#include "pch.h"
#include <stdio.h>
#include <winsock.h>
#pragma comment(lib,"wsock32")

#define  BUF_SIZE  200
#define PORT 10024
char wbuf[50], rbuf[100];
int main()
{
	char   buff[BUF_SIZE];
	SOCKET  s;
	int    len;
	WSADATA  wsadata;

	struct hostent *phe;      /*host information     */
	struct servent *pse;      /* server information  */
	struct protoent *ppe;     /*protocol information */
	struct sockaddr_in saddr;   /*endpoint IP address  */
	int   type;

	if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0)
	{
		printf("WSAStartup failed\n");
		WSACleanup();
		return -1;
	}
	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(PORT);
	saddr.sin_addr.s_addr = inet_addr("192.168.159.128");
	s = socket(PF_INET, SOCK_STREAM, 0);
	if (s == INVALID_SOCKET)
	{
		printf(" creat socket error \n");
		WSACleanup();
		return -1;
	}
	if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) == SOCKET_ERROR)
	{
		printf("connect socket  error \n");
		WSACleanup();
		return -1;
	}
	printf("please enter data:");
	fgets(wbuf, sizeof(wbuf), stdin);
	len = send(s, wbuf, sizeof(wbuf), 0);
	if (len < 0) perror("send failed");
	len = recv(s, rbuf, sizeof(rbuf), 0);
	if (len < 0) perror("recv failed");
	printf("server reply:%s\n", rbuf);
 
	closesocket(s);
	WSACleanup();
	return 0;
}

附录myhead.h

// myhead.h
#ifndef _MYHEAD_H
#define _MYHEAD_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/input.h>  //跟输入子系统模型有关的头文件
#include <dirent.h>
#include <stdbool.h>
#include <strings.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <poll.h>
#include <sys/epoll.h>
#endif

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值