Linux TCP Socket编程(二)

来源:互联网 发布:手机炒金属软件 编辑:程序博客网 时间:2024/05/22 05:09

篇一:LInux socket编程(一)
篇二:LInux socket编程(二)
前面通过多进程,多线程,支持处理多个客户端通信,本篇将讲述用select这个IO复用来实现。

IO模型

首先连接一下IO模型,unix下有5种可用IO模型,分别是:阻塞式IO、非阻塞式IO、IO复用、信号驱动式IO。


这里写图片描述

5种IO模型比较

1、阻塞式IO

阻塞式IO是最常用的IO模式,它就是没有数据就阻塞,一直等到有数据位置,期间啥也不干,就一门心思等数据。

2、非阻塞式IO

非阻塞式IO,就是不断轮询,问“数据有没有准备好啊”,“数据有没有准备好啊”….。数据准备好了,返回数据。不断询问,会耗费大量CPU时间。

3、IO复用模型

slect和pool就是IO复用。它是阻塞在系统调用,而不是阻塞在真正的IO系统调用上。学会转移矛盾,避免呢伤害了,自己很忙,不能阻塞自己,让别人帮忙等待(系统调用)。

4、信号驱动式IO模型

信号是个好东西,可以让内核在描述符就绪了,用信号(SIGIO)通知自己呀,这样不就工作学习两不误了。

5、异步IO模型

信号驱动IO是由内核通知我们何时可以启动一个IO操作,这个不通知我们何时启动,而是直接替我们工作,并由内核告诉我们何时可以完成。一个是何时开始,一个是何时完成。

select函数

slelect函数原型为:

int select(int maxfdp1,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

参数含义:

  • maxfdp1:指定待预测的描述符个数,它的值是待测试的最大描述符加1,描述符0,1,2,… ,maxfdp1-1;
  • readfds,writefds,exceptfds:指定我们要让内核测试读、写和异常条件的描述符;
  • timeout:告知内核等待就绪描述符可花多长时间(不等,等一段时间,永久等待)。

描述符操作:

void FD_ZERO(fd_set *fdset); //initialize the set:all bits offvoid FD_SET(int fd,fd_set *fdset);  //turn on the bit for fd in dfsetvoid FD_CLR(int fd,fd_set *fdset);  //turn off the bit for fd in dfsetint  FD_ISSET(int fd,fd_set *fdset);  // is the bit for fd on in fdset

了解了select的使用,下面就来用select实现echo服务器。

server select版

/**    Handle multiple socket connections with select and fd_set on Linux*/#include <stdio.h>#include <string.h>   //strlen#include <stdlib.h>#include <errno.h>#include <unistd.h>   //close#include <arpa/inet.h>    //close#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros#define TRUE   1#define FALSE  0#define PORT 8888#define BUF_SIZE 1205#define MAX_ENQ 1024int main(int argc , char *argv[]){    int opt = TRUE;    int listenfd ,connfd,max_fd,sockfd, activity,i ,valread ;    int client[FD_SETSIZE],max_client_index;    socklen_t socklen=sizeof(struct sockaddr_in);    char buffer[BUF_SIZE];  //data buffer of 1K    struct sockaddr_in servaddr,cliaddr;    //set of socket descriptors    fd_set rset,allset;    //initialise all client_socket[] to 0 so not checked    for (i = 0; i < FD_SETSIZE; i++)     {        client[i] = -1;    }    max_client_index=-1;    //create a master socket    if( (listenfd = socket(AF_INET , SOCK_STREAM , 0)) <0)     {        perror("socket failed");        exit(EXIT_FAILURE);    }    //set master socket to allow multiple connections , this is just a good habit, it will work without this    if( setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )    {        perror("setsockopt");        exit(EXIT_FAILURE);    }    //type of socket created    bzero(&servaddr,sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = INADDR_ANY;    servaddr.sin_port = htons( PORT );    //bind the socket to localhost port 8888    if (bind(listenfd, (struct sockaddr *)&servaddr, socklen)<0)     {        perror("bind failed");        exit(EXIT_FAILURE);    }    //try to specify maximum of 3 pending connections for the master socket    if (listen(listenfd, MAX_ENQ) < 0)    {        perror("listen");        exit(EXIT_FAILURE);    }    //accept the incoming connection    printf("echo server use select startup, listen on port %d\n", PORT);    printf("max connection: %d\n", FD_SETSIZE);    puts("Waiting for connections ...");      //clear the socket set    FD_ZERO(&allset);        //add master socket to set    FD_SET(listenfd, &allset);     max_fd = listenfd;    while(TRUE)     {        rset=allset;        //wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely        activity = select( max_fd + 1 , &rset , NULL , NULL , NULL);           if ((activity < 0) && (errno!=EINTR))         {            printf("select error");        }        //If something happened on the master socket , then its an incoming connection        if (FD_ISSET(listenfd, &rset))         {                      if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &socklen))<0)            {                perror("accept");                exit(EXIT_FAILURE);            }            //inform user of socket number - used in send and receive commands            printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , connfd , inet_ntoa(cliaddr.sin_addr) , ntohs(cliaddr.sin_port));            //send new connection greeting message            //a message            char *message = "good ,connect successfully \r\n";            if( send(connfd, message, strlen(message), 0) != strlen(message) )             {                perror("send");            }            //add new socket to array of sockets            for (i = 0; i < FD_SETSIZE; i++)             {                if( client[i] ==-1)                {                    client[i] = connfd;                                       break;                }            }            if (i == FD_SETSIZE)            {                fprintf(stderr, "too many connection, more than %d\n", FD_SETSIZE);                close(connfd);                continue;            }            if (connfd>max_fd)               max_fd=connfd;           if(i>max_client_index)               max_client_index=i;            FD_SET(connfd, &allset);  //一定不能丢,一定不能丢            if(--activity<=0)                continue;        }        //else its some IO operation on some other socket :)        for (i = 0; i <= max_client_index; i++)         {            if((sockfd = client[i])!=-1)            {                if (FD_ISSET( sockfd , &rset))                 {                    //Check if it was for closing , and also read the incoming message                    if ((valread = read( sockfd , buffer, BUF_SIZE)) == 0)                    {                        //Somebody disconnected , get his details and print                        getpeername(sockfd , (struct sockaddr*)&cliaddr , &socklen);                        printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(cliaddr.sin_addr) , ntohs(cliaddr.sin_port));                        //Close the socket and mark as 0 in list for reuse                        close(sockfd);                        FD_CLR(sockfd, &allset);                        client[i] = -1;                        continue;                    }                    else if (valread<0)                    {                        perror("read error");                        close(sockfd);                        FD_CLR(sockfd, &allset);                        client[i] = -1;                        continue;                    }                    //Echo back the message that came in                    else                     {                        printf("get message form client %d,meeasge is %s\n" , i,buffer);                        //set the string terminating NULL byte on the end of the data read                        buffer[valread] = '\0';                        send(sockfd , buffer , strlen(buffer) , 0 );                    }                    if(--activity<=0)                        break;                }            }                        }    }    return 0;} 

真是不容易,花了一个早上才调试通。测试结果如下:
服务器:


这里写图片描述

客户端1:

这里写图片描述

客户端2:

这里写图片描述

客户端3:

这里写图片描述

select学习就到这里了,接下来试着用pool和epool实现服务器,加油!!!!!

0 0
原创粉丝点击