Linux 高并发服务器--fork/pthread实现

1.多进程并发服务器

子进程来源于父进程,通俗的来讲,它是父进程的一个复制品。子进程完全复制了父进程的资源,包括进程上下文、内存信息、工作目录等信息。同时操作系统也会给子进程分配内存空间和调度资源。父进程和子进程的区分主要是靠fork()函数的返回值来进行。

  • 1.return 0 ;成功创建子进程,进入到子进程中相关操作

  • 2.return-1;创建子进程失败

  • 3.return (子进程)pid; 也即是(>0)进入到父进程,返回值为子进程的pid;此时可进入到父进程相关操作
    在Linux中实现多进程并发服务器,值得注意的一个问题就是子进程的回收问题,为了避免僵尸进程的出现,占用大量的系统资源,此处用signal()信号中断处理函数进行回收。

#include <sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<sys/types.h>
#include<strings.h>
#include<sys/wait.h>
#include"wrap.h"

#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666
//"192.168.32.30"

void *wait_child(int signo)//子进程回收函数
{
        while (waitpid(0,NULL,WNOHANG) > 0);
        return ;
}

int main(void)
{
        pid_t pid;
        int sfd,cfd;
        struct sockaddr_in serv_addr , clie_addr;
        socklen_t clie_addr_len,serv_addr_len;
        memset(&serv_addr,0,sizeof(serv_addr));
        memset(&clie_addr,0,sizeof(clie_addr));
        char buf[BUFSIZ];
        char clie_IP[BUFSIZ];
        int n;
        int i;
        sfd =  Socket(AF_INET,SOCK_STREAM,0);

         serv_addr.sin_family = AF_INET;//ipv4
         serv_addr.sin_port = htons(SERV_PORT);
         serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
         //inet_pton(AF_INET,"192.168.32.30",&serv_addr.sin_addr.s_addr);
         serv_addr_len = sizeof(serv_addr);
         Bind(sfd, (struct sockaddr*)&serv_addr, serv_addr_len);

         Listen(sfd,128);
         printf("wait for client connect");
         while(1)//创建子进程  
         {
                 clie_addr_len = sizeof(clie_addr);
                 cfd = Accept(sfd,(struct sockaddr*)&clie_addr,&clie_addr_len);
                 printf("cfd =  %d\n",cfd);
                 printf("cliet IP:%s,cliet port:%d\n",
                 inet_ntop(AF_INET,
                 &clie_addr.sin_addr.s_addr,
                 clie_IP,
                 sizeof(clie_IP)),
                 ntohs(clie_addr.sin_port));

                 pid = fork();
                if (pid <0)
                {
                         perror("fork error");
                         exit(1);
                }
                else if(pid ==0)
                {
                         close(sfd);
                         break;
                }else{
                        close(cfd);
                        signal(SIGCHLD,wait_child);
                }
        }
        if(pid ==0)//子进程操作
        {
                while(1)
                {
                        n = Read(cfd,buf,sizeof(buf));
                        if(n == 0)
                        {
                                close(cfd);
                                return 0;
                        }else if(n == -1)
                        {
                                perror("read error");
                                exit(1);
                        {
                                perror("read error");
                                exit(1);
                        }else
                        {
                                for(i = 0;i < n;i++)
                                {
                                        buf[i] = toupper(buf[i]);
                                }
                                        write(cfd,buf,n);
                                        write(STDOUT_FILENO,buf,n);
                        }
                }
        }
        return 0;
}

2.多线程并发服务器

多线程和多进程之间的区别在不过多讲述,一句话“进程是资源分配的最小单位,线程是CPU调度的最小单位”,就表明了最大的区别在与系统资源利用效率的不同。多进程占用内存多,切换复杂,CPU利用率低,但是它编程相对来比较简单,容易调试,同时可靠性也比多线程高,毕竟多个进程之间是不会产生影响的;而多线程占用内存少,切换简单,CPU利用率高,是开发高性能并发系统的首选。
Linux 多线程主要是用 pthread 函数来实现的。主要用到:
1.pthread_create()函数,其原型如下:

pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                            void *(*start_routine) (void *), void *arg);

参数依次为:线程id,线程参数,入口函数,线程入口函数的参数名字
特别注意的是(void *)类型指针,表示无类型指针,在传递的时候需要强转。

#include <sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
#include<pthread.h>
#include<fcntl.h>
#include"wrap.h"


#define MAXSIAE 10240
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666
//"192.168.32.30"

/*自定义一个结构体,将sockaddr_in与cfd捆绑*/
struct s_info{
                struct sockaddr_in clie_addr;
                int connectfd;
};


void *do_work(void *arg)//子线程处理函数
{

                int n,i;
                char buf[MAXSIAE];
                char str[INET_ADDRSTRLEN];//INET_ADDRSTRLEN 16

                struct s_info *ts = (struct s_info*)arg;

                while(1)
                {
                                n = Read(ts->connectfd,buf,MAXSIAE);//读客户端
                                if(n == 0)
                                {
                                        printf("the cline %d closed!",ts->connectfd);
                                        break;
                                }

                                for(i = 0;i < n;i++)
                                        {
                                                buf[i] = toupper(buf[i]);
                                        }
                                write(ts->connectfd,buf,n);
                                write(STDOUT_FILENO,buf,n);
                }

                Close(ts->connectfd);

int main(void)
{

                struct sockaddr_in serv_addr , clie_addr;
                int sfd,cfd;

                socklen_t clie_addr_len,serv_addr_len;

                char clie_IP[BUFSIZ];
                int i = 0;
                pthread_t tid;//创建线程
                struct s_info ts[256];//最大线程数创建结构体数组

                 sfd =  Socket(AF_INET,SOCK_STREAM,0);
                 bzero(&serv_addr,sizeof(serv_addr));//地址结构初始化
                 serv_addr.sin_family = AF_INET;//ipv4
                 serv_addr.sin_port = htons(SERV_PORT);
                 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
                 //inet_pton(AF_INET,"192.168.32.30",&serv_addr.sin_addr.s_addr);
                 serv_addr_len = sizeof(serv_addr);

                 Bind(sfd, (struct sockaddr*)&serv_addr, serv_addr_len);
                //printf("wait for client connect:::");
                 Listen(sfd,128);
                 printf("wait for client connect::\n");

         while(1)
         {
                 clie_addr_len = sizeof(clie_addr);
                 cfd = Accept(sfd,(struct sockaddr*)&clie_addr,&clie_addr_len);//阻塞监听
                 printf("cfd =  %d\n",cfd);                 
                ts[i].clie_addr = clie_addr;//取出接收到的客户端addr结构体
                ts[i].connectfd = cfd;//文件描述符
                //传递到pthead_create 子线程执行
                
               //线程管理,当线程达到最大数时候进行处理
                pthread_create(&tid,NULL,do_work,(void*)&ts[i]);
                pthread_detach(tid);//子线程分离,防止僵尸线程产生
                i++;
        }
        
        return 0;
} 

注意:本简易服务器另外采用wrap错误处理函数,为了使得主要代码逻辑更加清晰

猜你喜欢

转载自blog.csdn.net/GJQJFJ/article/details/105315662