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("connect_timeout() 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; }