Linux socket多进程服务器框架二

Posted 庖丁解牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux socket多进程服务器框架二相关的知识,希望对你有一定的参考价值。

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

#ifndef _vxclt
#define _vxclt

#ifdef __cplusplus
extern "C"
{
#endif

/**
 * clientsock_init - 初始化socket
 * @handle:socket句柄
 * 成功返回0,失败返回错误码
 * */
int clientsock_init(void **handle);

/**
 * connect_server - 客户端创建连接
 * @handle:socket句柄
 * @port:端口号
 * @ipaddr:请求服务器的IP地址
 * 成功返回0,失败返回错误码
 * */
int connect_server(void *handle, int port, char *ipaddr,
        unsigned int wait_seconds);

/**
 * clt_Process_business - 客户端业务处理
 * @handle:socket句柄
 * */
void clt_Process_business(void *handle);

/**
 * clt_socket_Destory - 释放socket句柄
 * @handle:socket句柄
 * */
void clt_socket_Destory(void **handle);

#ifdef __cplusplus
extern "C"
}
#endif
#endif
//clthelp.c    --客户端代码实现
#include "clthelp.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>

/**
 * clientsock_init - 初始化socket
 * @handle:socket句柄
 * 成功返回0,失败返回错误码
 * */
int clientsock_init(void **handle)
{
    int ret = 0;
    //初始化socket环境
    //创建socket套接字
    if (handle == NULL)
    {
        ret = Sck_ParamErr;
        printf("clientsock_init() params not correct !\n");
        return ret;
    }
    Mysock *mysock = (Mysock *) malloc(sizeof(Mysock));
    if (mysock == NULL)
    {
        ret = Sck_MacErr;
        printf("malloc() failed !");
        return ret;
    }
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        ret = Sck_BaseErr;
        perror("socket() err");
        return ret;
    }
    mysock->fd = sockfd;
    *handle=mysock;
    return 0;
}

/**
 * connect_server - 客户端创建连接
 * @handle:socket句柄
 * @port:端口号
 * @ipaddr:请求服务器的IP地址
 * 成功返回0,失败返回错误码
 * */
int connect_server(void *handle, int port, char *ipaddr,
        unsigned int wait_seconds)
{
    int ret = 0;
    if (handle == NULL || ipaddr == NULL || port < 0 || port > 65535)
    {
        ret = Sck_ParamErr;
        printf("getconnection() params not correct !\n");
        return ret;
    }
    Mysock *mysock = (Mysock *) handle;
    //connect
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ipaddr);
    ret = connect_timeout(mysock->fd, &addr, wait_seconds);
    if (ret == -1)
    {
        if (errno == ETIMEDOUT)
        {
            //超时处理
            ret = Sck_TimeoutErr;
            printf("connect_timeout() time out !\n");
            return ret;
        }
        ret = Sck_BaseErr;
        perror("bind() err");
        return ret;
    }
    return ret;
}

/**
 * clt_send - 客户端发送数据
 * @handle:socket句柄
 * @wait_seconds:等待超时秒数,如果为0表示不检测超时
 * 失败返回错误码
 * */
void clt_send(void *handle, unsigned int wait_seconds)
{
    int ret = 0;
    char buf[MAXBUFSIZE] = { 0 };
    Mysock *mysock = (Mysock *) handle;
    //从终端读取数据
    while (fgets(buf, MAXBUFSIZE, stdin) != NULL)
    {
        if (strlen(buf) > MAXBUFSIZE)
        {
            printf("输入字节数过长!\n");
            break;
        }
        //超时检测
        ret = write_timeout(mysock->fd, wait_seconds);
        if (ret == -1)
        {
            if (errno == ETIMEDOUT)
            {
                printf("write_timeout() time out !\n");
                break;
            }
            perror("write_timeout() err");
            break;
        } else
        {
            ret = socket_send(mysock->fd, buf, strlen(buf));
            if (ret == -1)
            {
                printf("socket_send() failed !\n");
                break;
            }
            memset(buf, 0, MAXBUFSIZE);
        }
    }
}

/**
 * clt_recv - 客户端接收数据
 * @handle:socket句柄
 * @wait_seconds:等待超时秒数,如果为0表示不检测超时
 * */
void clt_recv(void *handle, unsigned int wait_seconds)
{
    int ret = 0;
    char buf[MAXBUFSIZE] = { 0 };
    int len = MAXBUFSIZE;
    Mysock *mysock = (Mysock *) handle;
    while (1)
    {
        //超时检测
        ret = read_timeout(mysock->fd, wait_seconds);
        if (ret == -1)
        {
            if (errno == ETIMEDOUT)
            {
                printf("read_timeout() time out !\n");
                break;
            }
            perror("read_timeout() err");
            break;
        } else
        {
            len = MAXBUFSIZE;
            ret = socket_recv(mysock->fd, buf, &len);
            if (ret == -1)
            {
                break;
            }
            fputs(buf, stdout);
            memset(buf, 0, sizeof(buf));
        }
    }
}

/**
 * handler - 信号捕捉函数
 * @sign:信号码
 * */
void handler(int sign)
{
    if (sign == SIGCHLD)
    {
        int mypid = 0;
        while ((mypid = waitpid(-1, NULL, WNOHANG)) > 0)
        {
            printf("子进程的pid=%d\n", mypid);
        }
        //退出父进程
        exit(0);
    } else if (sign == SIGPIPE)
    {
        printf("accept SIGPIPE !\n");
    } else if (sign == SIGUSR1)
    {
        //父进程退出,子进程也需要退出
        exit(0);
    }
}

/**
 * clt_Process_business - 客户端业务处理
 * @handle:socket句柄
 * */
void clt_Process_business(void *handle)
{
    int ret = 0;
    if (handle == NULL)
    {
        ret = Sck_ParamErr;
        printf("Process_business() params not correct !\n");
        return;
    }
    //信号安装
    int signoarr[3] = { SIGCHLD, SIGPIPE, SIGUSR1 };
    ret = Install_Signal(signoarr, 3, handler);
    if (ret != 0)
    {
        return;
    }
    pid_t pid = fork();
    if (pid == -1)
    {
        ret = Sck_BaseErr;
        perror("fork() err");
        return;
    }
    if (pid == 0)
    {
        //子进程接收数据
        clt_recv(handle, 100);
        exit(0);
    } else if (pid > 0)
    {
        //父进程写数据
        clt_send(handle, 100);
        //向子进程发信号--就算发送出错,我也毫无办法去关闭子进程了
        kill(pid, SIGUSR1);
        exit(0);
    }
    return;
}

/**
 * clt_socket_Destory - 释放socket句柄
 * @handle:socket句柄
 * */
void clt_socket_Destory(void **handle)
{
    if (handle == NULL)
    {
        printf("clt_socket_Destory() param not correct !\n");
        return;
    }
    Mysock *mysock = (Mysock *) *handle;
    if (mysock != NULL)
    {
        printf("客户端执行释放函数!\n");
        free(mysock);
        *handle = NULL;
    }
    mysock = NULL;
}
//客户端
#include "clthelp.h"

int main()
{
    void *handle = NULL;
    int ret = 0;
    //初始化socket
    ret = clientsock_init(&handle);
    if (ret != 0)
    {
        printf("error message:%s\n", strsockerr(ret));
        return -1;
    }
    //连接服务器
    ret = connect_server(handle, 8080, "127.0.0.1", 70);
    if (ret != 0)
    {
        printf("error message:%s\n", strsockerr(ret));
        return -1;
    }
    //多进程发送接收信息
    clt_Process_business(handle);
    //释放内存
    clt_socket_Destory(&handle);
    return 0;
}

 


以上是关于Linux socket多进程服务器框架二的主要内容,如果未能解决你的问题,请参考以下文章

Linux socket多进程服务器框架三

linux C/C++多进程教程(多进程原理以及多进程的应用以多连接socket服务端为例(fork子进程处理socket_fd),同时介绍了僵尸进程产生原因与解决方法)(getpidfork)

Linux多进程的应用

c++ 网络编程TCP/IP linux 下多进程socket通信 多个客户端与单个服务端交互代码实现回声服务器

Linux C实现简单,多进程,多线程服务器

Linux 网络编程六(多进程服务器僵尸进程解决方案)