Chinaunix首页 | 论坛 | 博客
  • 博客访问: 841836
  • 博文数量: 150
  • 博客积分: 5123
  • 博客等级: 大校
  • 技术积分: 1478
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-06 10:03
文章分类

全部博文(150)

文章存档

2011年(2)

2010年(139)

2009年(9)

分类: C/C++

2010-09-01 22:19:23

转载于:http://blog.chinaunix.net/u/6889/showart.php?id=2296856

标题有些大.其实自己也是在不断的摸索
觉得BLOG的作用应该和日记一样
把自己获取到的东西记录下来
到时候回头看能找到自己跌倒过的地方避免再次跌倒
同时别人看的时候也能避开这些地方
所以在自己开始写服务程序一段时间之后
把原来的代码整理一下一次次的发上来
从零开始完全手写一个服务,经过慢慢的演化
期间的收获还是很多的

最开始,
程序是从最简单的socket连接开始
只有一个进程,创建socket之后开始listen
然后accept连上来的链接
整个程序是阻塞的.当一个链接上来之后
其他链接是无法连入的
之后当程序处理完并close之后,后面的才能被处理
这个例子是不停的接受客户端发过来的字符并且显示在服务端的console
然后客户端也不会被close.
只是演示概念
外面一个循环是不停的去accept新的连接
里面一个是循环处理已经accept上来链接的数据
由于没有close,所以外面一个循环是没意义的.呵呵
只是大家知道概念就OK了.

#include<sys/socket.h>
#include<netinet/in.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
    int listen_fd,accept_fd,flag;
    struct sockaddr_in my_addr,remote_addr;
    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){
        perror("create socket error");
        exit(1);
    }
    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){
        perror("setsockopt error");
    }
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(3389);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(my_addr.sin_zero),8);
    if ( bind( listen_fd, (struct sockaddr *)&my_addr,
                sizeof(struct sockaddr_in)) == -1 ) {
        perror("bind error");
        exit(1);
    }
    if ( listen( listen_fd,1 ) == -1 ){
        perror("listen error");
        exit(1);
    }
    for(;;){
        int addr_len = sizeof( struct sockaddr_in );
        accept_fd = accept( listen_fd,
                                (struct sockaddr *)&remote_addr,&addr_len );
        for(;;){
            char in_buf[1024];
            memset(in_buf, 0, 1024);
            recv( accept_fd ,&in_buf ,1024 ,0 );
            printf( "accept:%s\n", in_buf );#include

#include

#include

#include

#include

int main(){

    int listen_fd,accept_fd,flag;

    struct sockaddr_in my_addr,remote_addr;

    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){

        perror("create socket error");

        exit(1);

    }

    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){

        perror("setsockopt error");

    }

    my_addr.sin_family = AF_INET;

    my_addr.sin_port = htons(3389);

    my_addr.sin_addr.s_addr = INADDR_ANY;

    bzero(&(my_addr.sin_zero),8);

    if ( bind( listen_fd, (struct sockaddr *)&my_addr,

                sizeof(struct sockaddr_in)) == -1 ) {

        perror("bind error");

        exit(1);

    }

    if ( listen( listen_fd,1 ) == -1 ){

        perror("listen error");

        exit(1);

    }

    for(;;){

        int addr_len = sizeof( struct sockaddr_in );

        accept_fd = accept( listen_fd,

                                (struct sockaddr *)&remote_addr,&addr_len );

        for(;;){

            char in_buf[1024];

            memset(in_buf, 0, 1024);

            recv( accept_fd ,&in_buf ,1024 ,0 );

            printf( "accept:%s\n", in_buf );

        }

    }

    return 0;

}

        }
    }
    return 0;
}


从头编写高性能服务程序2-双进程阻塞

上一个模型的缺点就是
当链接数据没被处理完时,
整个服务是无法处理新链接的
这种比较适合单词处理速度很快
而且并发量=0的服务
也就是服务端和客户端1:1的样子
我丢给你请求,你给我数据,我再给你请求

这对于一个比较高性能的服务肯定是不可接受的
所以我们产生了一个想法
用父子进程,父进程管listen
有链接进来就fork一个子进程
子进程accept之后去处理.处理完之后就退出
注意,下面这段代码还是没close
所以上面所说的只能是做了一半
大家理解概念就好

#include<sys/socket.h>
#include<netinet/in.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
    int listen_fd,accept_fd,flag;
    struct sockaddr_in my_addr,remote_addr;
    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){
        perror("create socket error");
        exit(1);
    }
    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){
        perror("setsockopt error");
    }
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(3389);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(my_addr.sin_zero),8);
    if ( bind( listen_fd, (struct sockaddr *)&my_addr,
                sizeof(struct sockaddr_in)) == -1 ) {
        perror("bind error");
        exit(1);
    }
    if ( listen( listen_fd,1 ) == -1 ){
        perror("listen error");
        exit(1);
    }
    int pid;
    pid=fork();
    for(;;){
        if( pid==0 ){
            int addr_len = sizeof( struct sockaddr_in );
            accept_fd = accept( listen_fd,
                    (struct sockaddr *)&remote_addr,&addr_len );
            for(;;){
                char in_buf[1024];
                memset(in_buf, 0, 1024);
                recv( accept_fd ,&in_buf ,1024 ,0 );
                printf( "accept:%s\n", in_buf );
            }
        }
        else{
            //do nothing

        }
    }
    return 0;
}


fork产生子进程之后返回值
如果是0,那么这个进程就是子进程
如果-1,就是错误
否则就是产生子进程的pid,可以用这个pid来控制子进程,比如kill掉

大家实际用起来会发现和单进程阻塞一样
只能处理一个客户端连接.根本没好处么
呵呵.其实只是演示一下fork
然后好处还是有的.如果子进程因为处理request而挂了
主进程可以控制,然后再产生一个
所以这种样子比单进程要可靠一点
一个干活.一个监控

那么下一步就是对应每个链接产生进程


从头编写高性能服务程序3-多进程阻塞

多fork几次
那fork到底放在哪里呢?
因为阻塞的特性会中断进程的运行知道阻塞情况接触
所以我们把fork放在accept之后
这样有实际链接之后才会产生子进程
现在这个程序在多个客户端连上来之后都能有反应了

#include<sys/socket.h>
#include<netinet/in.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){#include

#include

#include

#include

#include

int main(){

    int listen_fd,accept_fd,flag;

    struct sockaddr_in my_addr,remote_addr;

    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){

        perror("create socket error");

        exit(1);

    }

    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){

        perror("setsockopt error");

    }

    my_addr.sin_family = AF_INET;

    my_addr.sin_port = htons(3389);

    my_addr.sin_addr.s_addr = INADDR_ANY;

    bzero(&(my_addr.sin_zero),8);

    if ( bind( listen_fd, (struct sockaddr *)&my_addr,

                sizeof(struct sockaddr_in)) == -1 ) {

        perror("bind error");

        exit(1);

    }

    if ( listen( listen_fd,1 ) == -1 ){

        perror("listen error");

        exit(1);

    }

    int pid;

    int addr_len = sizeof( struct sockaddr_in );

    for(;;){

        accept_fd = accept( listen_fd,

                    (struct sockaddr *)&remote_addr,&addr_len );

        pid=fork();

        if( pid==0 ){

            for(;;){

                char in_buf[1024];

                memset(in_buf, 0, 1024);

                recv( accept_fd ,&in_buf ,1024 ,0 );

                printf( "accept:%s\n", in_buf );

            }

        }

        else{

            //manager the process

        }

    }

    return 0;

}

    int listen_fd,accept_fd,flag;
    struct sockaddr_in my_addr,remote_addr;
    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){
        perror("create socket error");
        exit(1);
    }
    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){
        perror("setsockopt error");
    }
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(3389);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(my_addr.sin_zero),8);
    if ( bind( listen_fd, (struct sockaddr *)&my_addr,
                sizeof(struct sockaddr_in)) == -1 ) {
        perror("bind error");
        exit(1);
    }
    if ( listen( listen_fd,1 ) == -1 ){
        perror("listen error");
        exit(1);
    }
    int pid;
    int addr_len = sizeof( struct sockaddr_in );
    for(;;){
        accept_fd = accept( listen_fd,
                    (struct sockaddr *)&remote_addr,&addr_len );
        pid=fork();
        if( pid==0 ){
            for(;;){
                char in_buf[1024];
                memset(in_buf, 0, 1024);
                recv( accept_fd ,&in_buf ,1024 ,0 );
                printf( "accept:%s\n", in_buf );
            }
        }
        else{
            //manager the process

        }
    }
    return 0;
}


从头编写高性能服务程序4-单进程非阻塞select

前面都是阻塞的
当我们在accept或者recv的时候
文件描述符如果没有就绪
整个进程就会被挂起
浪费啊…如果只有就绪的时候才去处理,然后调用之后立刻返回
我们就可以继续做下面的工作了
所以我们引入非阻塞概念
记住,试用setsockopt是无法设置socket接口非阻塞的
只能用fd专用的fcntl去设置fd的非阻塞性质
需要include头文件fcntl.h

还有一点,select所监听的fd_set
在每次select之后就会被清掉
所以一开始我发现每次accept一个数据之后就再也获取不到数据了
然后才发现这点,而这个在我找的资料里面都没提到
所以要把FD_SET放在每次循环里面
我想这也是select性能比较低的原因之一

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main(){
    int listen_fd,accept_fd,flag;
    struct sockaddr_in my_addr,remote_addr;
    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){
        perror("create socket error");
        exit(1);
    }
    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){
        perror("setsockopt error");
    }
    int flags = fcntl(listen_fd, F_GETFL, 0);
    fcntl(listen_fd, F_SETFL, flags|O_NONBLOCK);
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(3389);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(my_addr.sin_zero),8);
    if ( bind( listen_fd, (struct sockaddr *)&my_addr,
                sizeof(struct sockaddr_in)) == -1 ) {
        perror("bind error");
        exit(1);
    }
    if ( listen( listen_fd,1 ) == -1 ){
        perror("listen error");
        exit(1);
    }
    fd_set fd_sets;
 
    int max_fd = listen_fd ;
    int events=0;
    int accept_fds[1024];
    int i = 0;
 
    for(;;){
        FD_ZERO( &fd_sets );
        FD_SET(listen_fd,&fd_sets);
        int k=0;
        for(k=0; k<=i; k++){
            FD_SET(accept_fds[k],&fd_sets);
        }
        events = select( max_fd + 1, &fd_sets, NULL, NULL, NULL );
        if( events ){
                printf("events:%d\n",events);
                if( FD_ISSET(listen_fd,&fd_sets) ){
                        printf("listen event :1\n");
                        int addr_len = sizeof( struct sockaddr_in );
                        accept_fd = accept( listen_fd,
                                                (struct sockaddr *)&remote_addr,&addr_len );
                        int flags = fcntl(accept_fd, F_GETFL, 0);
                        fcntl(accept_fd, F_SETFL, flags|O_NONBLOCK);
                        accept_fds[i] = accept_fd;
                        i++;
                        max_fd = accept_fd ;
                        printf("new accept fd:%d\n",accept_fd);
                }
                int j=0;
                for( j=0; j<=i; j++ ){
                    if( FD_ISSET(accept_fds[j],&fd_sets) ){
                        printf("accept event :%d\n",j);
                        char in_buf[1024];
                        memset(in_buf, 0, 1024);
                        recv( accept_fds[j] ,&in_buf ,1024 ,0 );
                        printf( "accept:%s\n", in_buf );
                    }
                }
        }
    }
    return 0;
}


从头编写高性能服务程序5-单进程非阻塞epoll

 

select已经是很古老的非阻塞复用I/O方式了
Linux下最常用的IO复用是epoll了
所以我们用单进程来实验epoll
epoll_wait和select一样
都是用一个fd的阻塞来代替一堆fd

下面程序有一个问题
由于使用了ET模式
所以当listen_fd有客户端连接上来时
只会被通知一次
如果是并发连接,如果只accept了一次
则第二个链接会被忽略
也就是真正高并发情况下.会出现丢客户端请求的情况

由于使用了复用IO
所以即使是单进程也能处理多客户端连接了

#include<sys/socket.h>
#include<netinet/in.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <fcntl.h>
#include<sys/epoll.h>
int main(){
    int listen_fd,accept_fd,flag;
    struct sockaddr_in my_addr,remote_addr;
    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){
        perror("create socket error");
        exit(1);
    }
    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){
        perror("setsockopt error");
    }
    int flags = fcntl(listen_fd, F_GETFL, 0);
    fcntl(listen_fd, F_SETFL, flags|O_NONBLOCK);
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(3389);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(my_addr.sin_zero),8);
    if ( bind( listen_fd, (struct sockaddr *)&my_addr,
                sizeof(struct sockaddr_in)) == -1 ) {
        perror("bind error");
        exit(1);
    }
    if ( listen( listen_fd,1 ) == -1 ){
        perror("listen error");
        exit(1);
    }
    struct epoll_event ev,events[20];
    int epfd = epoll_create(256);
    int ev_s=0;#include

#include

#include

#include

#include

#include

#include

int main(){

    int listen_fd,accept_fd,flag;

    struct sockaddr_in my_addr,remote_addr;

    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){

        perror("create socket error");

        exit(1);

    }

    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){

        perror("setsockopt error");

    }

    int flags = fcntl(listen_fd, F_GETFL, 0);

    fcntl(listen_fd, F_SETFL, flags|O_NONBLOCK);

    my_addr.sin_family = AF_INET;

    my_addr.sin_port = htons(3389);

    my_addr.sin_addr.s_addr = INADDR_ANY;

    bzero(&(my_addr.sin_zero),8);

    if ( bind( listen_fd, (struct sockaddr *)&my_addr,

                sizeof(struct sockaddr_in)) == -1 ) {

        perror("bind error");

        exit(1);

    }

    if ( listen( listen_fd,1 ) == -1 ){

        perror("listen error");

        exit(1);

    }

    struct epoll_event ev,events[20];

    int epfd = epoll_create(256);

    int ev_s=0;

    ev.data.fd=listen_fd;

    ev.events=EPOLLIN|EPOLLET;

    epoll_ctl(epfd,EPOLL_CTL_ADD,listen_fd,&ev);

    for(;;){

        ev_s = epoll_wait( epfd,events,20,500 );

        int i=0;

        for(i=0; i

            if(events[i].data.fd==listen_fd){

                printf("listen event :1\n");

                int addr_len = sizeof( struct sockaddr_in );

                accept_fd = accept( listen_fd,

                                        (struct sockaddr *)&remote_addr,&addr_len );

                int flags = fcntl(accept_fd, F_GETFL, 0);

                fcntl(accept_fd, F_SETFL, flags|O_NONBLOCK);

                ev.data.fd=accept_fd;

                ev.events=EPOLLIN|EPOLLET;

                epoll_ctl(epfd,EPOLL_CTL_ADD,accept_fd,&ev);

                printf("new accept fd:%d\n",accept_fd);

            }

            else if(events[i].events&EPOLLIN){

                printf("accept event :%d\n",i);

                char in_buf[1024];

                memset(in_buf, 0, 1024);

                recv( events[i].data.fd ,&in_buf ,1024 ,0 );

                printf( "accept:%s\n", in_buf );

            }

        }

   }

    return 0;

}

#include
#include
#include
#include
#include
#include
#include
int main(){
    int listen_fd,accept_fd,flag;
    struct sockaddr_in my_addr,remote_addr;
    if ( (listen_fd = socket( AF_INET,SOCK_STREAM,0 )) == -1 ){
        perror("create socket error");
        exit(1);
    }
    if ( setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag)) == -1 ){
        perror("setsockopt error");
    }
    int flags = fcntl(listen_fd, F_GETFL, 0);
    fcntl(listen_fd, F_SETFL, flags|O_NONBLOCK);
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(3389);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(my_addr.sin_zero),8);
    if ( bind( listen_fd, (struct sockaddr *)&my_addr,
                sizeof(struct sockaddr_in)) == -1 ) {
        perror("bind error");
        exit(1);
    }
    if ( listen( listen_fd,1 ) == -1 ){
        perror("listen error");
        exit(1);
    }
    struct epoll_event ev,events[20];
    int epfd = epoll_create(256);
    int ev_s=0;
    ev.data.fd=listen_fd;
    ev.events=EPOLLIN|EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_ADD,listen_fd,&ev);
    for(;;){
        ev_s = epoll_wait( epfd,events,20,500 );
        int i=0;
        for(i=0; i
            if(events[i].data.fd==listen_fd){
                printf("listen event :1\n");
                int addr_len = sizeof( struct sockaddr_in );
                accept_fd = accept( listen_fd,
                                        (struct sockaddr *)&remote_addr,&addr_len );
                int flags = fcntl(accept_fd, F_GETFL, 0);
                fcntl(accept_fd, F_SETFL, flags|O_NONBLOCK);
                ev.data.fd=accept_fd;
                ev.events=EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,accept_fd,&ev);
                printf("new accept fd:%d\n",accept_fd);
            }
            else if(events[i].events&EPOLLIN){
                printf("accept event :%d\n",i);
                char in_buf[1024];
                memset(in_buf, 0, 1024);
                recv( events[i].data.fd ,&in_buf ,1024 ,0 );
                printf( "accept:%s\n", in_buf );
            }
        }
   }
    return 0;
}

    ev.data.fd=listen_fd;
    ev.events=EPOLLIN|EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_ADD,listen_fd,&ev);
    for(;;){
        ev_s = epoll_wait( epfd,events,20,500 );
        int i=0;
        for(i=0; i<ev_s; i++){
            if(events[i].data.fd==listen_fd){
                printf("listen event :1\n");
                int addr_len = sizeof( struct sockaddr_in );
                accept_fd = accept( listen_fd,
                                        (struct sockaddr *)&remote_addr,&addr_len );
                int flags = fcntl(accept_fd, F_GETFL, 0);
                fcntl(accept_fd, F_SETFL, flags|O_NONBLOCK);
                ev.data.fd=accept_fd;
                ev.events=EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,accept_fd,&ev);
                printf("new accept fd:%d\n",accept_fd);
            }
            else if(events[i].events&EPOLLIN){
                printf("accept event :%d\n",i);
                char in_buf[1024];
                memset(in_buf, 0, 1024);
                recv( events[i].data.fd ,&in_buf ,1024 ,0 );
                printf( "accept:%s\n", in_buf );
            }
        }
   }
    return 0;
}



阅读(1229) | 评论(3) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-12-09 13:30:24

你的第一个程序,为什么直接创建了10个进程,而不是有一个新的连接,创建一个进程?? 你这样做,不是很费资源

chinaunix网友2010-09-04 14:44:07

Download More than 1000 free IT eBooks: http://free-ebooks.appspot.com

chinaunix网友2010-09-03 15:39:28

Download More than 1000 free IT eBooks: http://free-ebooks.appspot.com