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 !\n");
        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 !\n");
        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 !\n");
    }
}

/**
 * run_server - 运行服务器
 * @listenfd:文件描述符
 * */
void run_server(int listenfd)
{
    int ret = 0;
    //屏蔽SIGPIPIE信号
    if (signal(SIGPIPE, handler) == SIG_ERR)
    {
        printf("signal() failed !\n");
        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\n", 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\n", strsockerr(ret));
        return -1;
    }
    //监听
    ret = listen_socket(sockfd, 8080);
    if (ret != 0)
    {
        printf("error message:%s\n", 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 [email protected] -c $<
clean:
    rm -f $(OBJS1)
    rm -f $(OBJS2)
    rm -f $(EXEC1)
    rm -f $(EXEC2)
				
时间: 2024-08-04 21:58:32

Linux socket多进程服务器框架三的相关文章

Linux socket多进程服务器框架一

重点:socket共用方法中错误码的定义以及错误码的解析 底层辅助代码 //serhelp.h #ifndef _vxser #define _vxser #ifdef __cplusplus extern "C" { #endif /** * sersocket_init - socket初始化 * @listenfd:文件描述符 * 成功返回0,失败返回错误码 * */ int sersocket_init(int *listenfd); /** * listen_socket -

Linux socket多进程服务器框架二

客户端未解决Bug:子进程或者父进程退出的时候,我无法做到两个进程都调用clt_socket_Destory()方式释放socket句柄, 但是进程退出后,相应的资源也会释放,有一定影响,但是不大,以后我想到办法再优化.重点:客户端connect服务器方法需要单独分离出来,方便用户自己断线重连. 客户端 //clthelp.h #include <stdio.h> #include "commsocket.h" #ifndef _vxclt #define _vxclt #

鸟哥的 Linux 私房菜(服务器) 第三章 局域网络架构简介 第四章 连上 Internet

鸟哥的 Linux 私房菜(服务器) 第三章 局域网络架构简介 第四章 连上 Internet [TOC] 3.1 局域网络的联机 3.1.1 局域网络的布线规划 3.1.1-1 Linux 直接联网-与 PC 同地位 3.1.1-2 Linux 直接联网-与一般 PC 分开网域 3.1.1-3 Linux 直接联网-让 Linux 直接管理 LAN 3.1.1-4 Linux 放在防火墙后-让 Linux 使用 Private IP 3.2 本书使用的内部联机网络参数与通讯协议 3.2.1 联

LINUX环境并发服务器的三种实现模型

服务器设计技术有很多,按使用的协议来分有TCP服务器和UDP服务器.按处理方式来分有循环服务器和并发服务器. 1  循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器,为了处理客户的请求,对服务端的程序就提出了特殊的要求. 目前最常用的服务器模型有: ·循环服务器:服务器在同一时刻只能响应一个客户端的请求 ·并发服务器:服务器在同一时刻可以响应多个客户端的请求 1.1 UDP循环服务器的实现方法: UDP循环服务器每次从套接字上读取一个客户端的请求->处理->然后将

详解Linux搭建vsftp服务器通过三种方式实现文件传输

概述 FTP(File Transfer Protocol)中文称为"文件传输协议".用于Internet上的控制文件的双向传输. 工作原理 一.主动模式: 1.客户端通过用户名和密码登录服务器端,登录的是21端口(服务器端主动开启的).2.服务器端通过21端口接收到客户端的访问,验证用户名和密码.3. 登陆成功,客户端会随机开启一个1024以上的端口,在端口上会传递一个叫port的命令,通过命令告知服务器,打开端口,向客户端传递数据.(顺便的会将随机端口号告知服务器)4.服务器接收之

PHP socket 服务器框架集

1.Swoole:重新定义PHP PHP语言的高性能网络通信框架,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询.Swoole可以广泛应用于互联网.移动通信.企业软件.网络游戏.物联网.车联网.智能家庭等领域. 使用PHP+Swoole作为网络通信框架,可以使企业IT研发团队的效率大大提升,更加专注于开发创新产品. 2.workerman workerman是一个高性能的PHP

Linux socket 类封装 (面向对象方法)

1 /* 2 * socketfactory.h 3 * 4 * Created on: 2014-7-19 5 * Author: root 6 */ 7 8 #ifndef SOCKETFACTORY_H_ 9 #define SOCKETFACTORY_H_ 10 #include<sys/types.h> 11 12 /* 13 * 在网路编程中, 一般分为服务端和客户端,两者的行为有相似之处,也有非常多的不同.在linux中对socket程序设计 14 * 仅提供了socket(),

Linux 网络编程——并发服务器的三种实现模型

服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器,按处理方式来分有循环服务器和并发服务器. 循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器(多对一),为了处理客户的请求,对服务端的程序就提出了特殊的要求. 目前最常用的服务器模型有: ·循环服务器:服务器在同一时刻只能响应一个客户端的请求 ·并发服务器:服务器在同一时刻可以响应多个客户端的请求 UDP 循环服务器的实现方法 UDP 循环服务器每次从套接字上读取一个客户端的请求 -> 处理

Skynet服务器框架(一) Linux下的安装和启动

根据云风博客的描述,Skynet 的核心功能就是解决一个问题: 把一个符合规范的 C 模块,从 动态库(so文件)中启动起来,绑定一个永不重复(即使模块退出)的数字id做为其 handle.模块 被称为 服务(Service),服务间可以自由发送消息. 每个 模块 可以向 Skynet 框架注册一个 callback 函数,用来接收发给它的消息: 每个服务都是被一个个 消息包 驱动,当没有包到来的时候,它们就会处于 挂起状态,此状态对 CPU 资源零消耗.如果需要自主逻辑,则可以利用 Skyne