zoukankan      html  css  js  c++  java
  • Linux socket多进程服务器框架三

    在使用select管理服务器连接的时候:
    注意1:select是可中断睡眠函数,需要屏蔽信号
    注意2:必须获取select的返回值nread,每次处理完一个事件,nread需要-1
    注意3:如果客户端的连接超过连接池的大小,需要关闭客户端连接
    注意4:获取最大套接字的方法是每次有客户端连接过来时,在和maxfd比较,这样就不用每次select之前都遍历池,查找最大值
    服务器
    //serhelp.h
    
    #ifndef _vxser
    #define _vxser
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    
    /**
     * sersocket_init - socket初始化
     * @listenfd:文件描述符
     * 成功返回0,失败返回错误码
     * */
    int sersocket_init(int *listenfd);
    
    /**
     * listen_socket - 绑定端口号,监听套接字
     * @listenfd:文件描述符
     * @port:绑定的端口号
     * 成功返回0,失败返回错误码
     * */
    int listen_socket(int listenfd, int port);
    
    /**
     * run_server - 运行服务器
     * @listenfd:文件描述符
     * */
    void run_server(int listenfd);
    
    #ifdef __cplusplus
    extern "C"
    }
    #endif
    #endif
    //serhelp.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/wait.h>
    #include <signal.h>
    #include "commsocket.h"
    
    /**
     * sersocket_init - socket初始化
     * @listenfd:文件描述符
     * 成功返回0,失败返回错误码
     * */
    int sersocket_init(int *listenfd)
    {
        int ret = 0;
        if (listenfd == NULL)
        {
            ret = Sck_ParamErr;
            printf("sersocket_init() params not correct !
    ");
            return ret;
        }
        //初始化socket环境
        int fd = socket(AF_INET, SOCK_STREAM, 0);
        if (fd == -1)
        {
            ret = Sck_BaseErr;
            perror("socket() err");
            return ret;
        }
        *listenfd = fd;
        return ret;
    }
    
    /**
     * listen_socket - 绑定端口号,监听套接字
     * @listenfd:文件描述符
     * @port:绑定的端口号
     * 成功返回0,失败返回错误码
     * */
    int listen_socket(int listenfd, int port)
    {
        int ret = 0;
        if (listenfd < 0 || port < 0 || port > 65535)
        {
            ret = Sck_ParamErr;
            printf("listen_socket() params not correct !
    ");
            return ret;
        }
        //reuse addr
        int optval = 1;
        ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval,
                sizeof(optval));
        if (ret == -1)
        {
            ret = Sck_BaseErr;
            perror("setsockopt() err");
            return ret;
        }
        //bind
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        ret = bind(listenfd, (struct sockaddr *) &addr, sizeof(addr));
        if (ret == -1)
        {
            ret = Sck_BaseErr;
            perror("bind() err");
            return ret;
        }
        //listen
        ret = listen(listenfd, SOMAXCONN);
        if (ret == -1)
        {
            ret = Sck_BaseErr;
            perror("listen() err");
            return ret;
        }
        return ret;
    }
    
    /**
     * handler - 信号捕捉函数
     * @sign:信号码
     * */
    void handler(int sign)
    {
        if (sign == SIGPIPE)
        {
            printf("server accept SIGPIPE !
    ");
        }
    }
    
    /**
     * run_server - 运行服务器
     * @listenfd:文件描述符
     * */
    void run_server(int listenfd)
    {
        int ret = 0;
        //屏蔽SIGPIPIE信号
        if (signal(SIGPIPE, handler) == SIG_ERR)
        {
            printf("signal() failed !
    ");
            return;
        }
        //定义文件描述符集合
        fd_set allsets;
        FD_ZERO(&allsets);
        fd_set readfds;
        FD_ZERO(&readfds);
        //定义客户端套接字池
        char socketPond[128] = { 0 };
        int i = 0;
        for (i = 0; i < 128; i++)
        {
            socketPond[i] = -1;
        }
        //定义池子最后一个元素的下标
        int maxindex = 0;
        //定义文件描述符中值最大的fd
        int maxfd = listenfd;
        //将监听套接字加入到集合中
        FD_SET(listenfd, &allsets);
        struct sockaddr_in peeraddr;
        socklen_t peerlen = sizeof(struct sockaddr_in);
        //定义接收缓冲区
        char buf[MAXBUFSIZE] = { 0 };
        int buflen = 0;
        int conn = 0;
        int nread = 0;
        while (1)
        {
            readfds = allsets;
            do
            {
                nread = select(maxfd + 1, &readfds, NULL, NULL, NULL);
            } while (nread == -1 && errno == EINTR);//屏蔽信号(重点)
            if (nread == -1)
            {
                perror("select() err");
                return;
            } else if (nread == 0)
            {
                //超时
                continue;
            } else if (nread > 0)
            {
                //执行操作
                //1.处理服务器监听套接字
                if (FD_ISSET(listenfd, &readfds))
                {
                    //客户端有连接完成三次握手
                    memset(&peeraddr, 0, sizeof(struct sockaddr_in));
                    peerlen = sizeof(struct sockaddr_in);
                    conn = accept(listenfd, (struct sockaddr *) &peeraddr,
                            &peerlen);
                    if (conn == -1)
                    {
                        perror("accept() err");
                        break;
                    }
                    printf("accept from %s
    ", inet_ntoa(peeraddr.sin_addr));
                    //将客户端套接字加入池子中
                    for (i = 0; i < 128; i++)
                    {
                        if (socketPond[i] == -1)
                        {
                            socketPond[i] = conn;
                            //数组最大下标后移
                            if (maxindex <= i)
                                maxindex = i + 1;
                            break;
                        }
                    }
                    //如果超过最大连接数,直接关闭连接(重点)
                    if (i == 128)
                    {
                        close(conn);
                        continue;
                    }
                    //将客户端套接字加入到监听集合中
                    FD_SET(conn, &allsets);
                    //每新加一个连接,就更新最大套接字(重点)
                    if(conn>maxfd)
                        maxfd=conn;
                    //需要处理的事件数-1(重点)
                    if (--nread <= 0)
                        continue;
                }
                //2.客户端读事件
                for (i = 0; i < maxindex; i++)
                {
                    if (socketPond[i] == -1)
                        continue;
                    if (FD_ISSET(socketPond[i], &readfds))
                    {
                        //接收客户端信息
                        memset(buf, 0, sizeof(buf));
                        buflen = MAXBUFSIZE;
                        ret = socket_recv(socketPond[i], buf, &buflen);
                        if (ret == -1)
                        {
                            //接收信息出错,关闭套接字
                            close(socketPond[i]);
                            //将该套接字移除池子
                            socketPond[i] = -1;
                            //将该套接字移除监听集合
                            FD_CLR(conn, &allsets);
                        } else
                        {
                            //打印信息
                            fputs(buf, stdout);
                            //向客户端发送数据
                            ret = socket_send(socketPond[i], buf, buflen);
                            if (ret == -1)
                            {
                                //发送数据出错,关闭套接字
                                close(socketPond[i]);
                                //将该套接字移除池子
                                socketPond[i] = -1;
                                //将该套接字移除监听集合
                                FD_CLR(conn, &allsets);
                            }
                        }
                        //处理的事件数-1
                        if (--nread <= 0)
                            break;
                    }
                }
            }
        }
        return;
    }
    //服务器
    #include "serhelp.h"
    #include <stdio.h>
    #include "commsocket.h"
    
    int main()
    {
        int ret = 0;
        int sockfd = 0;
        //初始化socket
        ret = sersocket_init(&sockfd);
        if (ret != 0)
        {
            printf("error message:%s
    ", strsockerr(ret));
            return -1;
        }
        //监听
        ret = listen_socket(sockfd, 8080);
        if (ret != 0)
        {
            printf("error message:%s
    ", strsockerr(ret));
            return -1;
        }
        //运行
        run_server(sockfd);
        return 0;
    }
    
    
    .SUFFIXES:.c .o
    CC=gcc
    SRCS1=mserver.c
        serhelp.c
        commsocket.c
        sockhelp.c
    OBJS1=$(SRCS1:.c=.o)
    EXEC1=mser
    SRCS2=mclient.c
        clthelp.c
        commsocket.c
        sockhelp.c
    OBJS2=$(SRCS2:.c=.o)
    EXEC2=mcl
    
    start:$(OBJS1) $(OBJS2)
        $(CC) -o $(EXEC1) $(OBJS1)
        $(CC) -o $(EXEC2) $(OBJS2)
        @echo "--------OK-----------"
    .c.o:
        $(CC) -Wall -g -o $@ -c $<
    clean:
        rm -f $(OBJS1)
        rm -f $(OBJS2)
        rm -f $(EXEC1)
        rm -f $(EXEC2)
    
    
  • 相关阅读:
    Jquery实现无刷新DropDownList联动
    Mvc 提交表单的4种方法全程详解
    Sql Like 通配符 模糊查询技巧及特殊字符
    SQL 语句递归查询 With AS 查找所有子节点
    最常用的五类CSS选择器
    取出分组后每组的第一条记录(不用group by)按时间排序
    SpringCloud中接收application/json格式的post请求参数并转化为实体类
    SpringCloud负载均衡笔记
    iview-admin打包笔记
    SpringCloud之最大的坑
  • 原文地址:https://www.cnblogs.com/zhanggaofeng/p/6181707.html
Copyright © 2011-2022 走看看