#ifndef __SERVER_H__#define __SERVER_H__#include "my_socket.h"#include <sys/stat.h>#include <sys/types.h>#include <fcntl.h>#include <sys/time.h>#include <sys/select.h>#include <sys/uio.h>#include <sys/wait.h>#include <errno.h>#define SER_IP "127.0.0.1"#define SER_PORT 8888#define ST_BUSY 1#define ST_IDLE 2#define SIZE 8192#define MSG_SIZE (SIZE - 4)typedef struct tag_mag{ int msg_len ; char msg_buf[MSG_SIZE];//8188}MSG, *pMSG;typedef struct tag_chd{ int s_sfd ; int s_state ;}NODE, *pNODE;extern int errno ;void make_child(pNODE arr, int cnt);void child_main(int sfd) ;void handle_request(int sfd);void send_fd(int sfd, int fd_file) ;void recv_fd(int sfd, int* fd_file) ;void dispatch(pNODE arr, int cnt, int fd_client);#endif
#include "server.h"int main(int argc, char* argv[])//exe chld_cnt{ if(argc != 2) { printf("Usage: exe , child_cnt! \n"); exit(1); } int child_cnt = atoi(argv[1]); pNODE arr_child = (pNODE)calloc(child_cnt, sizeof(NODE)) ; /* 动态数组维护子进程池 */ make_child(arr_child, child_cnt); int fd_listen, fd_client ; my_socket(&fd_listen, MY_TCP, SER_IP, SER_PORT); my_listen(fd_listen, 10); fd_set readset, readyset ; FD_ZERO(&readset); FD_ZERO(&readyset); FD_SET(fd_listen, &readset);//先将监听客户端的描述符设置到监听集合 int index ; for(index = 0; index < child_cnt; index ++) { FD_SET(arr_child[index].s_sfd, &readset);//再将和子进程的通信的socket设置到监听集合 } int select_ret ; struct timeval tm ; while(1) { tm.tv_sec = 0 ; tm.tv_usec = 1000 ; readyset = readset ; select_ret = select(1024, &readyset, NULL, NULL, &tm); if(select_ret == 0) /* 轮询时间内,所有描述符均没有活动,返回0,继续轮询 */ { continue ; }else if(select_ret == -1) /* 信号 */ { if(errno == EINTR) { continue ; }else { exit(1); } }else { if(FD_ISSET(fd_listen, &readyset))//如果是客户端发来的信号 { fd_client = accept(fd_listen, NULL, NULL) ; dispatch(arr_child, child_cnt ,fd_client); close(fd_client); } for(index = 0; index < child_cnt; index ++) { if(FD_ISSET(arr_child[index].s_sfd, &readyset))//是子进程发来的信号 { int val ; read(arr_child[index].s_sfd, &val, 4); arr_child[index].s_state = ST_IDLE ; } } } } }
#include "server.h"void make_child(pNODE arr, int cnt){ int index ; for(index = 0; index < cnt; index ++) { pid_t pid ; int fds[2] ;//fds[0] - c fds[1] - p socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); pid = fork() ; if(pid == 0)// child { close(fds[1]); /* 子进程用fds[0],关闭fds[1] */ child_main(fds[0]) ; /* 每创建一个子进程,子进程就进入该函数中(死循环),接收请求,处理请求,如此循环。*/ }else { /* 初始化进程池队列中的每一个子进程 */ arr[index].s_sfd = fds[1] ; arr[index].s_state = ST_IDLE ; close(fds[0]); /* 父进程用fds[1], 关闭fds[0] */ } }}void child_main(int sfd){ int fd_client ; int flag ; int readn ; pid_t pid = getpid(); while(1) { readn = read(sfd, &flag, 4); printf("readn: %d \n", readn); printf("read from father: %d \n", flag); recv_fd(sfd, &fd_client);//从父进程中读出客户端的描述符 handle_request(fd_client); write(sfd, &pid, sizeof(pid)); }}void handle_request(int sfd){ MSG my_msg ; int recvn ; while(1) { memset(&my_msg, 0, sizeof(MSG)); my_recv(&recvn, sfd, &my_msg, 4); if(my_msg.msg_len == 0)//如果客户端退出 { break ; } my_recv(NULL, sfd, my_msg.msg_buf, my_msg.msg_len); my_send(NULL, sfd, &my_msg, my_msg.msg_len + 4); }}
//父进程传文件描述符给子进程void send_fd(int sfd, int fd_file) { struct msghdr my_msg ; memset(&my_msg, 0, sizeof(my_msg)); struct iovec bufs[1] ; char buf[32] = "hello world ! \n"; bufs[0].iov_base = buf ; bufs[0].iov_len = strlen(buf) ; my_msg.msg_name = NULL ; my_msg.msg_namelen = 0 ; my_msg.msg_iov = bufs ; my_msg.msg_iovlen = 1 ; my_msg.msg_flags = 0 ; struct cmsghdr *p ; int cmsg_len = CMSG_LEN(sizeof(int)) ; /* 所传为文件描述符,因此sizeof(int) */ p = (struct cmsghdr*)calloc(1, cmsg_len) ; p -> cmsg_len = cmsg_len ; p -> cmsg_level = SOL_SOCKET ; p -> cmsg_type = SCM_RIGHTS ; *(int*)CMSG_DATA(p) = fd_file ; my_msg.msg_control = p ; my_msg.msg_controllen = cmsg_len ; int sendn ; sendn = sendmsg(sfd, &my_msg, 0); printf("send masg len : %d \n", sendn);}void recv_fd(int sfd, int* fd_file) { struct msghdr my_msg ; struct iovec bufs[1] ; char buf1[32]="" ; bufs[0].iov_base = buf1 ; bufs[0].iov_len = 31 ; my_msg.msg_name = NULL ; my_msg.msg_namelen = 0 ; my_msg.msg_iov = bufs ; my_msg.msg_iovlen = 2 ; my_msg.msg_flags = 0 ; struct cmsghdr *p ; int cmsg_len = CMSG_LEN(sizeof(int)) ; p = (struct cmsghdr*)calloc(1, cmsg_len) ; my_msg.msg_control = p ; my_msg.msg_controllen = cmsg_len ; int recvn ; recvn = recvmsg(sfd, &my_msg, 0); *fd_file = *(int*)CMSG_DATA((struct cmsghdr*)my_msg.msg_control); //写成*(int*)CMSG_DATA(P)也可 printf("buf1: %s, recv msg len : %d \n", buf1, recvn);}void dispatch(pNODE arr, int cnt, int fd_client){ int index ; for(index = 0 ; index < cnt; index ++) { if(arr[index].s_state == ST_IDLE) { write(arr[index].s_sfd, &index, 4); send_fd(arr[index].s_sfd, fd_client); /* 向空闲的子进程分配任务,将服务器accept返回的socket描述符发送给子进程*/ arr[index].s_state = ST_BUSY ; break ; } }}
#include "my_socket.h"#define MY_IP "127.0.0.1"#define MY_PORT 6666#define SER_IP "127.0.0.1"#define SER_PORT 8888#define SIZE 8192#define MSG_SIZE (SIZE - 4)typedef struct tag_mag// { int msg_len ; char msg_buf[MSG_SIZE];//8188}MSG, *pMSG;int main(int argc, char* argv[]){ int sfd ; my_socket(&sfd, MY_TCP, MY_IP, atoi(argv[1])); my_connect(sfd, SER_IP, SER_PORT); MSG my_msg ; while(memset(&my_msg, 0, sizeof(MSG)), fgets(my_msg.msg_buf, MSG_SIZE, stdin)!= NULL) { my_msg.msg_len = strlen(my_msg.msg_buf); my_send(NULL, sfd, &my_msg, 4 + my_msg.msg_len ); memset(&my_msg, 0, sizeof(MSG)); my_recv(NULL, sfd, &my_msg, 4); my_recv(NULL, sfd, &my_msg.msg_buf, my_msg.msg_len); printf("recv from server : %s \n", my_msg.msg_buf); } /* 客户端退出时,向服务器发送一个长度为0的消息 ,用于通知服务器退出 */ memset(&my_msg, 0, sizeof(MSG)); my_send(NULL, sfd, &my_msg, 4 + my_msg.msg_len); close(sfd);}