多进程服务器,多线程服务器

目录

1、多进程服务器

2、多线程服务器

1、多进程服务器

使用多进程并发服务器时要考虑以下几点:

1.父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符)

2.系统内创建进程个数(与内存大小相关)

3.进程创建过多是否降低整体服务性能(进程调度)

1、建立套接字

int socketFd = socket

2、绑定

3、设置监听

while(1){

4、阻塞等待客户端的连接......

int newClientFd = accept();

//创建一个子进程,子进程负责接收每一个连接上来的客户端数据

pid_t id = fork();

if(id == 0) //子进程{

//关闭 socketFd 文件描述符

while(1) {

//接收 客户端的数据

int ret = read(newClientFd,buf,sizeof(buf));

if(ret == 0)

break; }

//子进程退出的时候 ,会自动发出来一个SIGCHLD信号

break; }

else if(id >0) //父进程

{ //关闭 newClientFd 文件描述符

//捕捉 SIGCHLD信号 执行 信号响应函数,在该函数中 回收子进程

continue;}

}

 多进程服务器测试代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> //close
#include <sys/wait.h>
#include <signal.h>
#include <sys/types.h>   //socket       /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_PORT 60000
#define SERVER_IP   "192.168.5.184"

//子进程退出信号处理函数
void handle(int arg)
{
    wait(NULL);
    printf("子进程退出,回收资源OK\n");
}

int main()
{
     //建立套接字
    int socket_fd;
    //AF_INET-地址族ipv4 SOCK_SREAM-->tcp字节流
    socket_fd = socket(AF_INET,SOCK_STREAM,0);
    if(socket_fd < 0)
    {
        perror("soket fail");
        return -1;
    }   
    
    //所以设置端口号可以复用,这两条语句放在 绑定bind 之前
    int optval = 1; 
    setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));

    //填充本机ip地址和端口号(新结构体)
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;//地址族 ipv4
    server_addr.sin_port = htons(SERVER_PORT); //本机端口转网络端口--用宏来替代好修改
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//本机ip转换为网络ip

    //绑定本机ip(旧结构体)
    int ret;
    ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in));
    if(ret < 0)
    {
        perror("bind fail");
        return -1;
    }
    printf("绑定本机成功 [%s][%d]\n",SERVER_IP,SERVER_PORT);

    //监听
    ret = listen(socket_fd,20);
    if(ret < 0)
    {
        perror("listen fail");
        return -1;
    }

    //并发接收客户端--重复接收(让代码accept之后下次还停留在accpet)
    while(1)
    {
        //接收客户端(获取客户端的套接字、IP地址、端口)--->阻塞
        int socket_cli; //存储客户端的套接字
        struct sockaddr_in client_addr; //只定义不需要赋值
        socklen_t addrlen = sizeof(struct sockaddr_in);
        socket_cli = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);
        if(socket_cli < 0)
        {
            perror("accept fail");
        }
        //拿到客户端的地址和端口号(将网络字节序转换为本机字节序)---难点
        char *ip = inet_ntoa(client_addr.sin_addr);//客户端的IP地址 网络ip->本机ip
        int port = ntohs(client_addr.sin_port);//客户端的端口号 //网络端口->本机端口
        printf("新的客户端上线[%s][%d] socket_cli:%d\n",ip,port,socket_cli);      

        //创建子进程用来接收客户端数据
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork fail");
            return -1;
        }
        else if(id > 0) //父进程---父进程里面不能阻塞否则无法接收下一个客户端
        {
            //回收子进程资源
            signal(SIGCHLD,handle); //信号处理函数
        }
        else //子进程独立接收客户端信息
        {
            char buf[1024] = {0};
            while(1)
            {
                memset(buf,0,sizeof(buf));
                ret = recv(socket_cli,buf,sizeof(buf),0); //等价read
                if(ret == 0)
                {
                    printf("客户端掉线[%s][%d]\n",ip,port);
                    exit(0); //子进程退出会给父进程发送SIGCHLD
                }
                printf("[%s][%d] buf:%s ret:%d\n",ip,port,buf,ret);
            }

        }

    }    


    return 0;
}

2、多线程服务器

void*routine(void*arg)

{

//设置 分离属性 --自动回收

while(1){ //接收 客户端的数据

                int ret = read(newClientFd,buf,sizeof(buf));

                if(ret == 0)

                break;

1、建立套接字

int socketFd = socket();

2、绑定

3、设置监听

while(1){

        4、阻塞等待客户端的连接

        int newClientFd = accept();

        //每次一个新的客户端连接上来,开启一条线程,接收 客户端的数据 25         pthread_create(,,routine,&newClientFd);

}

 多线程服务器测试代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#define SERVER_IP   "192.168.5.2"
#define SERVER_PORT 60000

/*
练习1:修改代码,实现多线程并发服务器
练习2:将客户端的ip和端口在子线程中能够打印
*/

struct recv_info
{
    int sock_fd;
    struct sockaddr_in cli_addr;
};

//将recv接收数据的这一个阻塞动作放在子线程里面
void *recv_buf(void *arg)
{
    //设置子线程的分离属性--注意
    pthread_detach(pthread_self());

    int ret;
    struct recv_info info = *(struct recv_info *)arg; //接收套接字和地址

    //接收数据recv-->阻塞
    char buf[1024] = {0};
    while(1)
    {
        memset(buf,0,sizeof(buf));
        ret = recv(info.sock_fd,buf,sizeof(buf),0);
        if(ret == 0)
        {
            printf("客户端掉线[%s][%d]\n",
                    inet_ntoa(info.cli_addr.sin_addr),
                    ntohs(info.cli_addr.sin_port));
            close(info.sock_fd); //关闭客户端套接字
            pthread_exit(NULL); //客户端掉线线程退出
            //printf("客户端掉线[%s][%d]\n",ip,port);
        }
        //printf("buf:%s ret:%d\n",buf,ret);
        printf("[%s][%d] buf:%s ret:%d\n",
            inet_ntoa(info.cli_addr.sin_addr), //网络ip转本机ip
            ntohs(info.cli_addr.sin_port), //网络端口哦转本机端口
            buf,
            ret);
    }
}

int main()
{
    int ret;

    //建立套接字
    int socket_fd;
    socket_fd = socket(AF_INET,SOCK_STREAM,0);
    if(socket_fd < 0)
    {
        perror("socket fail");
        return -1;
    }

    //设置端口复用
    int optval = 1; 
    setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));   

    //绑定本机地址和端口
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);//htons是转端口
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //htonl是转地址,常用于广播和组播。
    ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in));
    if(ret < 0)
    {
        perror("bind fail");
        return -1;
    }
    printf("绑定本机成功[%s][%d]\n",SERVER_IP,SERVER_PORT);
    
    //监听
    ret = listen(socket_fd,20);
    if(ret < 0)
    {
        perror("listen fail");
        return -1;
    }

    /*
        多进程并发服务器和多线程并发服务器的核心知识点:
        防止accept和recv两个阻塞函数放在同一个while(1)里面。
        多进程并发:用一个单独进程放recv (消耗资源多)
        多线程并发:用一个单独线程放recv  (消耗资源少)  
    */
    //服务器循环接收客户端那么while(1)要放在accept的前面
    while(1)
    {
        //接收客户端accpet-->阻塞
        int socket_client;
        struct sockaddr_in client_addr;
        socklen_t addrlen = sizeof(struct sockaddr_in);
        socket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);
        char *ip = inet_ntoa(client_addr.sin_addr);
        int port = ntohs(client_addr.sin_port);
        printf("新的客户端上线[%s][%d] socket_client:%d\n",ip,port,socket_client);


        struct recv_info info;
        memset(&info,0,sizeof(struct recv_info));//清空一个结构体
        info.sock_fd = socket_client;
        info.cli_addr = client_addr;

        //在accept后面创建子线程
        pthread_t pid;
        //ret = pthread_create(&pid,NULL,recv_buf,&socket_client);
        ret = pthread_create(&pid,NULL,recv_buf,&info);
        if(ret < 0)
        {
            perror("pthread_create fail");
        }
    }

    //关闭套接字
    close(socket_fd);

    return 0;
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值