- 浏览: 12811 次
最近访客 更多访客>>
文章分类
最新评论
-
javay:
宇宙大将军 写道liuguly 写道建议学MS的东西,java ...
JAVA新手,开始起航~~ -
宇宙大将军:
liuguly 写道建议学MS的东西,java的不要学了对啊, ...
JAVA新手,开始起航~~ -
liuguly:
建议学MS的东西,java的不要学了
JAVA新手,开始起航~~ -
rosolio:
还是别干java 了!没前途……
JAVA新手,开始起航~~ -
weixl003:
java语言相比c c++好学的多,建议你多看一下面向对象的编 ...
JAVA新手,开始起航~~
这里讲的仅仅是一个简单的server的模型!为了处理同时来到很多小的链接请求( 解释:就是请求很简单,持续时间很短,那么if server在请求到来时在fork来处理它,有可能fork的时间比应答请求的还要少,那么就是不合理的服务设计 ),所以我们采用的是“prefork”和“prethread”模型!
Unix 网络编程 上的4个模型是:prefork:主进程accept
子进程accept
prethread:
主线程accept
子线程accept ( 姑且使用主线程和子线程来描述 )
第一部分是:使用“预先生成进程”处理
CODE_1 : server是:主进程accept,那么这是4种方法中最复杂的,因为要涉及到进程间传递socket描述符的问题!( 进程间传递描述符在上一篇bolg中有过 !),server采用轮询的方式将socket传递给子进程!
话不多说,贴上代码:
Server:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/select.h> #include <sys/types.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/epoll.h> #include <fcntl.h> #define PORT 6000 #define MAXBACK 100 #define MAXLINE 1024 #define CHILD_NUM 10 typedef struct child_process { pid_t s_pid; //!> 子进程的pid int s_pipe_fd; //!> 与子进程通信的pipe口 int s_status; //!> 子进程的状态!0:闲 1:忙 }child_process; child_process child[CHILD_NUM]; //!> 定义10个子进程( 此处以10个为例 ) static int n_child_use = 0; //!> 几个child在工作( if 全忙就不给他们 ) //!> 发送socket描述符( 这个代码在上一篇博文上有 ) //!> int send_fd( int fd_send_to, void * data, size_t len, int sock_fd ) { struct msghdr msghdr_send; //!> the info struct struct iovec iov[1]; //!> io vector size_t n; //!> union { struct cmsghdr cm; //!> control msg char ctl[CMSG_SPACE(sizeof( int ))]; //!> the pointer of char }ctl_un; struct cmsghdr * pCmsghdr = NULL; //!> the pointer of control msghdr_send.msg_control = ctl_un.ctl; msghdr_send.msg_controllen = sizeof( ctl_un.ctl ); //!> design : the first info pCmsghdr = CMSG_FIRSTHDR( &msghdr_send ); //!> the info of head pCmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); //!> the msg len pCmsghdr->cmsg_level = SOL_SOCKET; //!> -> stream mode pCmsghdr->cmsg_type = SCM_RIGHTS; //!> -> file descriptor *((int *)CMSG_DATA( pCmsghdr )) = sock_fd; //!> data: the file fd //!> these infos are nosignification msghdr_send.msg_name = NULL; //!> the name msghdr_send.msg_namelen = 0; //!> len of name iov[0].iov_base = data; //!> no data here iov[0].iov_len = len; //!> the len of data msghdr_send.msg_iov = iov; //!> the io/vector info msghdr_send.msg_iovlen = 1; //!> the num of iov return ( sendmsg( fd_send_to, &msghdr_send, 0 ) ); //!> send msg now } //!> 接收socket描述符 //!> int recv_sock_fd( int fd, void * data, size_t len, int * recv_fd ) { struct msghdr msghdr_recv; //!> the info struct struct iovec iov[1]; //!> io vector size_t n; //!> union { struct cmsghdr cm; //!> control msg char ctl[CMSG_SPACE(sizeof( int ))]; //!> the pointer of char }ctl_un; struct cmsghdr * pCmsghdr = NULL; //!> the pointer of control msghdr_recv.msg_control = ctl_un.ctl; msghdr_recv.msg_controllen = sizeof( ctl_un.ctl ); //!> these infos are nosignification msghdr_recv.msg_name = NULL; //!> the name msghdr_recv.msg_namelen = 0; //!> len of name iov[0].iov_base = data; //!> no data here iov[0].iov_len = len; //!> the len of data msghdr_recv.msg_iov = iov; //!> the io/vector info msghdr_recv.msg_iovlen = 1; //!> the num of iov if( ( n = recvmsg( fd, &msghdr_recv, 0 ) ) < 0 ) //!> recv msg { //!> the msg is recv by msghdr_recv printf("recv error : %d\n", errno); exit(EXIT_FAILURE); } //!> now, we not use 'for' just because only one test_data_ if( ( pCmsghdr = CMSG_FIRSTHDR( &msghdr_recv ) ) != NULL //!> now we need only one, && pCmsghdr->cmsg_len == CMSG_LEN( sizeof( int ) ) //!> we should use 'for' when ) //!> there are many fds { if( pCmsghdr->cmsg_level != SOL_SOCKET ) { printf("Ctl level should be SOL_SOCKET :%d \n", errno); exit(EXIT_FAILURE); } if( pCmsghdr->cmsg_type != SCM_RIGHTS ) { printf("Ctl type should be SCM_RIGHTS : %d\n", errno); exit(EXIT_FAILURE); } *recv_fd =*((int*)CMSG_DATA(pCmsghdr)); //!> get the data : the file des* } else { *recv_fd = -1; } return n; } //!> 子进程具体的执行过程 //!> void web_child( int con_fd ) { char buf[MAXLINE]; int n_read; int i = 0; while( strcmp( buf, "Q" ) != 0 && strcmp( buf, "q" ) != 0 ) { memset( buf, 0, sizeof( buf ) ); if( ( n_read = read( conn_fd, buf, MAXLINE ) ) < 0 ) { printf( "Read errnr! :%d \n", errno ); exit( EXIT_FAILURE ); } else if( n_read == 0 ) { continue; } else { while( buf[i] ) { buf[i] = toupper( buf[i] ); i++; } buf[i] = '\0'; printf("Child %d done! \n", ( unsigned int )pthread_self()); printf("Child %d send %s\n", ( unsigned int )pthread_self(), buf); write( conn_fd, buf, strlen( buf ) ); //!> 写回给client } } printf("Child %d : Dating end!\n", ( unsigned int )pthread_self()); } //!> child process 的主函数 //!> void child_main( int i ) { char data; //!> 由于此处我们主要是传递socket,那么data一般就给一个” “做一个标志就好 int con_fd; //!> 接受con_fd int n_read; //!> 读取长度 printf( "Child %d starting ... \n", i ); while( 1 ) { if( ( n_read = recv_sock_fd( STDERR_FILENO, &data, 1, &con_fd ) ) == 0 ) { continue; //!> 此处理论上应该是阻塞,但是简化为轮询 //printf( " Child process %d read errnr! : %d\n", i, errno ); //exit( EXIT_FAILURE ); } if( con_fd < 0 ) { printf("Child %d read connfd errnr! : %d\n", i, errno); exit( EXIT_FAILURE ); } web_child( con_fd ); //!> child具体的执行过程 write( STDERR_FILENO, " ", 1 ); //!> 随便写点什么让server知道我处理完成了,那么就可以将状态位置为0了 } } //!> 产生子进程及相关处理 //!> void child_make( int i, int listen_fd ) { int sock_fd[2]; //!> 为了和主进程通信创建socket pair pid_t pid; //!> 创建 socketpair if( socketpair( AF_LOCAL, SOCK_STREAM, 0, sock_fd ) == -1 ) { printf( "create socketpair error : %d\n", errno ); exit( EXIT_FAILURE ); } if( ( pid = fork() ) > 0 ) //!> 父进程 { close( sock_fd[1] ); child[i].s_pid = pid; child[i].s_pipe_fd = sock_fd[0]; child[i].s_status = 0; return; } if( dup2( sock_fd[0], STDERR_FILENO ) == -1 ) //!> 现在可以使用STDERR_FILENO替换刚刚创建的sock描述符 { //!> 往后的child的操作就可以STDERR_FILENO中进行! printf("socket pair errnr! : %d\n", errno); exit( EXIT_FAILURE ); } close( sock_fd[0] ); //!> 这些描述符都bu需要了! close( sock_fd[1] ); close( listen_fd ); child_main( i ); //!> child 主循环 } //!> MAIN PROCESS //!> int main( int argc, char ** argv ) { int i; int listen_fd; int conn_fd; int max_fd; int n_select; int n_read; char buf[5]; fd_set all_set, now_set; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; int len = sizeof( struct sockaddr_in ); //!> server 套接口 //!> bzero( &servaddr, sizeof( servaddr ) ); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl( INADDR_ANY ); servaddr.sin_port = htons( PORT ); //!> 建立套接字 if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { printf("Socket Error...\n" , errno ); exit( EXIT_FAILURE ); } //!> 绑定 //!> if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( servaddr ) ) == -1 ) { printf("Bind Error : %d\n", errno); exit( EXIT_FAILURE ); } //!> 监听 //!> if( listen( listen_fd, MAXBACK ) == -1 ) { printf("Listen Error : %d\n", errno); exit( EXIT_FAILURE ); } FD_ZERO( &all_set ); FD_SET( listen_fd, &all_set ); //!> 将listenfd加入select max_fd = listen_fd; for( i = 0; i < CHILD_NUM; i++ ) { child_make( i, listen_fd ); FD_SET( child[i].s_pipe_fd, &all_set ); //!> 将子进程socket加入 max_fd = max_fd > child[i].s_pipe_fd ? max_fd : child[i].s_pipe_fd; } while( 1 ) //!> 主进程循环 { now_set = all_set; if( n_child_use >= CHILD_NUM ) //!> 没有可以使用的child 了 { //!> 那么就将listenfd从中清空,也就是不在响应listen了,直到有child空闲 FD_CLR( listen_fd, &now_set ); } if( (n_select = select( max_fd + 1, &now_set, NULL, NULL, NULL )) == -1) { printf(" Main process select errnr~ :%d\n", errno); exit( EXIT_FAILURE ); } if( FD_ISSET( listen_fd, &now_set ) ) //!> if来了请求 { if( ( conn_fd = accept( listen_fd, ( struct sockaddr *)&cliaddr , &len ) ) == -1 ) { printf("Server accept errnr! : %d\n", errno); exit( EXIT_FAILURE ); } for( i = 0; i < CHILD_NUM; i++ ) { if( child[i].s_status == 0 ) //!> 此child闲置 { break; } } if( i == CHILD_NUM ) //!> 说明child已经全部处于忙态 { printf("All childs are busy! \n"); exit( EXIT_FAILURE ); //!> 此处可以等待哦,或者丢弃数据 } child[i].s_status = 1; //!> busy n_child_use++; //!> busy child ++ send_fd( child[i].s_pipe_fd, " ", 1, conn_fd ); //!> 发送socket描述符 close( conn_fd ); //!> server不需要处理了 if( --n_select == 0 ) //!> 没有其他的请求了 { continue; } } for( i = 0; i < CHILD_NUM; i++ ) //!> 看看那些child发来了msg,其实server知道肯定是child完成处理的提示标志 { if( FD_ISSET( child[i].s_pipe_fd, &now_set ) ) { if( ( n_read = read( child[i].s_pipe_fd, buf, 5 ) ) == 0 ) //!> 这里的buf中data没有用,仅仅是child告诉server我完成了 { printf("Child %d exit error! : %d\n", i, errno); exit( EXIT_FAILURE ); } child[i].s_status = 0; //!> 状态位置闲 if( --n_select == 0 ) //!> if没有其他child回送消息就不要浪费时间for了 { break; } } } } return 0; } Client: [cpp] view plaincopyprint? #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/select.h> #define MAXLINE 1024 #define SERV_PORT 6000 //!> 注意输入是由stdin,接受是由server发送过来 //!> 所以在client端也是需要select进行处理的 void send_and_recv( int connfd ) { FILE * fp = stdin; int lens; char send[MAXLINE]; char recv[MAXLINE]; fd_set rset; FD_ZERO( &rset ); int maxfd = ( fileno( fp ) > connfd ? fileno( fp ) : connfd + 1 ); //!> 输入和输出的最大值 int n; while( 1 ) { FD_SET( fileno( fp ), &rset ); FD_SET( connfd, &rset ); //!> 注意不要把rset看作是简单的一个变量 //!> 注意它其实是可以包含一组套接字的哦, //!> 相当于是封装的数组!每次都要是新的哦! if( select( maxfd, &rset, NULL, NULL, NULL ) == -1 ) { printf("Client Select Error..\n"); exit(EXIT_FAILURE ); } //!> if 连接口有信息 if( FD_ISSET( connfd, &rset ) ) //!> if 连接端口有信息 { printf( "client get from server ...\n" ); memset( recv, 0, sizeof( recv ) ); n = read( connfd, recv, MAXLINE ); if( n == 0 ) { printf("Recv ok...\n"); break; } else if( n == -1 ) { printf("Recv error...\n"); break; } else { lens = strlen( recv ); recv[lens] = '\0'; //!> 写到stdout write( STDOUT_FILENO, recv, MAXLINE ); printf("\n"); } } //!> if 有stdin输入 if( FD_ISSET( fileno( fp ), &rset ) ) //!> if 有输入 { //!> printf("client stdin ...\n"); memset( send, 0, sizeof( send ) ); if( fgets( send, MAXLINE, fp ) == NULL ) { printf("End...\n"); exit( EXIT_FAILURE ); } else { //!>if( str ) lens = strlen( send ); send[lens-1] = '\0'; //!> 减一的原因是不要回车字符 //!> 经验值:这一步非常重要的哦!!!!!!!! if( strcmp( send, "q" ) == 0 ) { printf( "Bye..\n" ); return; } printf("Client send : %s\n", send); write( connfd, send, strlen( send ) ); } } } } int main( int argc, char ** argv ) { //!> char * SERV_IP = "10.30.97.188"; char buf[MAXLINE]; int connfd; struct sockaddr_in servaddr; if( argc != 2 ) { printf("Input server ip !\n"); exit( EXIT_FAILURE ); } //!> 建立套接字 if( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { printf("Socket Error...\n" , errno ); exit( EXIT_FAILURE ); } //!> 套接字信息 bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1], &servaddr.sin_addr); //!> 链接server if( connect( connfd, ( struct sockaddr * )&servaddr, sizeof( servaddr ) ) < 0 ) { printf("Connect error..\n"); exit(EXIT_FAILURE); } /*else { printf("Connet ok..\n"); }*/ //!> //!> send and recv send_and_recv( connfd ); //!> close( connfd ); printf("Exit\n"); return 0; } CODE_2 : server 是“子进程accept”,那么就是要比第一个简单一点,但是有一个新的问题就是,子进程要“抢”accept,那么必然存在一个“互斥”accept的问题,当然我么可以使用文件锁,但是效率比较低,而且在此处只是一个实验,所以姑且就使用“线程互斥量”,看起来有点不和谐,呵呵呵!~ Server: [cpp] view plaincopyprint? /* 基本思路: server预先创建几个子进程,由子进程进行互斥accept, 由于此处使用文件锁的效率会低一点,那么就使用互斥量 代替! 当然这是不协调的,这里只是简单处理! */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <sys/select.h> #include <sys/types.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/epoll.h> #include <fcntl.h> #define PORT 6000 #define MAXBACK 100 #define MAXLINE 1024 #define CHILD_NUM 10 pthread_mutex_t g_mutex; //!> 互斥量 pthread_mutexattr_t g_mattr ; //!> 属性 //!> 产生子进程及相关处理 //!> void child_make( int i_num, int listen_fd ) { int i = 0; pid_t pid; int conn_fd; int n_read; char buf[MAXLINE]; struct sockaddr_in cliaddr; int len = sizeof( struct sockaddr_in ); if( ( pid = fork() ) > 0 ) { return; } while( 1 ) { pthread_mutex_lock( &g_mutex ); //!> 加锁 if( ( conn_fd = accept( listen_fd, ( struct sockaddr *)&cliaddr , &len ) ) == -1 ) { printf("Accept errnr! :%d\n", errno); exit( EXIT_FAILURE ); } pthread_mutex_unlock( &g_mutex ); //!> 解锁 if( ( n_read = read( conn_fd, buf, MAXLINE ) ) < 0 ) { printf( "Read errnr! :%d \n", errno ); exit( EXIT_FAILURE ); } else if( n_read == 0 ) { continue; } else { while( buf[i] ) { buf[i] = toupper( buf[i] ); i++; } printf("Child %d done! \n", i_num); printf("Child %d send %s\n", i_num, buf); write( conn_fd, buf, strlen( buf ) ); //!> 写回给client } } } //!> MAIN PROCESS //!> int main( int argc, char ** argv ) { int i; int listen_fd; int conn_fd; int n_read; char buf[5]; struct sockaddr_in servaddr; //!>下面需要设置进程间共享此锁 //!> pthread_mutexattr_init( &g_mattr ); pthread_mutexattr_setpshared( &g_mattr, PTHREAD_PROCESS_SHARED ); pthread_mutex_init( &g_mutex, &g_mattr ); //!> 初始化 //!> server 套接口 //!> bzero( &servaddr, sizeof( servaddr ) ); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl( INADDR_ANY ); servaddr.sin_port = htons( PORT ); //!> 建立套接字 if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { printf("Socket Error...\n" , errno ); exit( EXIT_FAILURE ); } //!> 绑定 //!> if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( servaddr ) ) == -1 ) { printf("Bind Error : %d\n", errno); exit( EXIT_FAILURE ); } //!> 监听 //!> if( listen( listen_fd, MAXBACK ) == -1 ) { printf("Listen Error : %d\n", errno); exit( EXIT_FAILURE ); } for( i = 0; i < CHILD_NUM; i++ ) { child_make( i, listen_fd ); } waitpid( 0 ); //!> 等待所有子进程而已 pthread_mutex_destroy( &g_mutex ); //!> 删除互斥灯 return 0; } Client:和上面的一样!!!
相关推荐
《Linux服务器配置与管理》课程设计任务书 本课程设计任务书的主要目的是让学生熟练掌握 Linux 操作系统的使用,并掌握 Linux 下各类网络服务器软件的安装及配置。通过完成本课程设计,学生将能够综合了解 Linux ...
基于Linux的Web服务器的...本文对基于Linux的Web服务器的设计与实现进行了详细的分析和讨论,涵盖了Linux操作系统、Apache服务器、DHPC配置等多个方面,旨在帮助读者更好地理解Web服务器设计与实现的基本概念和要点。
本文将围绕“Linux高性能服务器设计”这一主题展开讨论,具体分析其关键技术和设计思路。 #### 二、问题背景 - **C10K问题**:指的是单个服务器同时处理1万个连接的能力。早期Linux系统由于采用了基于阻塞I/O和进程...
设计一个高性能的服务器程序是非常重要的,然而传统的服务器工作方式存在三个显著的缺点:fork开销大、进程间通信困难、并发执行能力差。为了解决这些问题,文章介绍了一种基于Linux下的多线程服务器程序设计方法。 ...
总的来说,设计一个应用于物联网的Linux云端服务器需要综合运用TCP/IP协议、并发处理、数据安全以及服务器资源管理等技术,以满足物联网环境下大数据处理和实时通信的需求。这样的服务器不仅能够提高数据处理效率,...
"基于Linux的服务器集群系统设计及实现" 本文主要探讨了基于Linux的服务器集群系统的设计及实现。集群系统是指将多台服务器连接起来,提供高可用性、高性能和易管理性的计算资源。Linux操作系统作为一个类UNIX系统...
设计一个Linux FTP服务器,你需要了解以下关键知识点: 1. **FTP协议基础**:FTP是Internet上最早的服务之一,用于在客户端和服务器之间传输文件。它基于TCP/IP协议族,分为控制连接和数据连接两个部分,通过端口号...
Linux下L2TP网络服务器设计.pdf
这个课程设计涵盖了从基础服务安装到高级配置优化的全过程,对于学习Linux服务器管理和Web开发的初学者来说是一次宝贵的实践经验。通过实际操作,学生能够深入了解Linux环境下的Web服务架构,提高对网络操作系统原理...
开题报告(基于Linux系统云服务器的搭建与设计).docx开题报告(基于Linux系统云服务器的搭建与设计).docx开题报告(基于Linux系统云服务器的搭建与设计).docx开题报告(基于Linux系统云服务器的搭建与设计).docx开题报告...
Linux系统下Web服务器的搭建与设计 本文主要介绍了在 Linux 环境下使用 Apache 及 Tomcat 软件作为服务器的核心,搭建一个全能且稳定的 WEB 服务器。同时还对服务器进行简单优化,相对降低服务器的系统资源占用率,...
Linux环境并发服务器设计技术研究.pdf
《Linux高性能服务器编程》这本书是针对那些希望深入理解并掌握Linux环境下服务器开发技术的专业人士而编写的。书中详细探讨了如何利用C和C++这两种语言,实现高效且可靠的服务器应用程序。以下是该书可能涵盖的一些...
标题“linux服务器客户端1对1聊天”表明我们将构建一个简单的聊天系统,其中服务器能够与单个客户端进行通信。这个系统的核心部分包括服务器端(server1.c)和客户端(client1.c)的代码。 1. **服务器端(server1.c...
Linux 服务器搭建 Linux 服务器搭建是指在 Linux 操作系统上搭建一个服务器,提供各种网络服务,满足不同用户的需求。...只有通过合适的规划和设计,才能搭建一个稳定、安全、高效的 Linux 服务器。
8. **课程设计模板**:`linux操作系统与程序设计课程设计模板.docx`可能包含了一个详细的步骤指南,从需求分析、设计思路到代码实现和测试,为学生提供了一个完整的参考框架。 通过这个项目,不仅可以深入理解网络...
Linux 下 WMV 高性能 IPTV 流媒体服务器的设计与开发 本文主要介绍了 Linux 下 WMV 高性能 IPTV 流媒体服务器的设计与开发。该服务器采用创新 的主从式架构设计,并设计了 RTSP 连接队列调度等算法,用来完善服务器...