socket编程(3-2)

在上一节中,我们使用了REUSEADDR,解决了服务器重启不能立即使用的问题。这一节,我们来解决多个客户端连接服务器的问题。

 

 

首先分析一下,之前的服务器为什么不可以接收多个客户端的连接请求。

原因是:
已有一个客户端连接后 ,服务器处于while中,没有机会来处理accept

解决方法:fork
思路:子进程处理一个连接,父进程继续回去监听,处理accept

对于父进程来说,他不需要处理已连接进程conn,这时候我们要想到父子进程共享文件描述符的。子进程继承了父进程的文件描述符,对于父进程来说他不需要处理连接,将连接套接口关闭掉close(conn);
对于子进程来说,他不需要处理监听,将监听套接字关闭掉close(listenfd)

 

来看一下服务器代码:

#include<stdio.h>
#include <sys/socket.h>
#include<sys/types.h>
#include<unistd.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include<stdlib.h>
#include<errno.h>
#include<string.h>

void do_service(int conn)
{
    
        /*处理通信的细节*/
        char recvbuf[1024];
    while(1)
        {
        memset(recvbuf,0,sizeof(recvbuf));
        int ret  = read(conn,recvbuf,sizeof(recvbuf));
    if(ret == 0)
    {
        printf("client close");
        break;
    }
        fputs(recvbuf,stdout);
        write(conn,recvbuf,ret);

        }
}
int main()
{
    /*create a socket*/
    int listenfd;
    if((listenfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
        printf("socket err");
    
    /*init addr*/    
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
    /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/

    int on = 1;/*开启地址重复利用*/
    if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)
        printf("setsockopt err");

    /*将创建的套接字与本地地址进行绑定*/
    if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
        printf("blind err");

    /*监听套接字*/
    if (listen(listenfd,SOMAXCONN) < 0 )
        printf("listen err:");
    
    
    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof(peeraddr);
    int conn;
    
    pid_t pid;
    while(1)
        {

        /*主动套接字*/
        if ((conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0 )
            printf("accept err");
     /*当客户端断开时,服务器可以捕捉到*/
        printf("ip = %s,port = %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
        
        pid = fork();
        if(pid == -1)
            printf("fork err");
        else if(pid == 0)
        {
            close(listenfd);
            do_service(conn);    
            exit(EXIT_SUCCESS);
        }
        else
        {
            close(conn);
        }
    
    }

    return 0;

}

客户端代码没有修改:

#include<stdio.h>
#include <sys/socket.h>
#include<sys/types.h>
#include<unistd.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include<stdlib.h>
#include<errno.h>
#include<string.h>


int main()
{
    /*创建套接字*/    
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
        printf("socket err");

    /*serv addr and port*/    
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    /*连接,一旦连接成功,sockfd套接字就处于已连接状态,从逻辑上
      是与服务器端的conn套接字连接*/    
    if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0)
        printf("connect err");
    
    
    char sendbuf[1024]={0};
    char recvbuf[1024]={0};
    while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
    {
        write(sockfd,sendbuf,strlen(sendbuf));
        read(sockfd,recvbuf,sizeof(recvbuf));
    
        fputs(recvbuf,stdout);
        memset(sendbuf,0,sizeof(sendbuf));
        memset(recvbuf,0,sizeof(recvbuf));
        
    }

    close(sockfd);

    return 0;

}

现在,我们已经可以处理多个客户端的连接了。并且,当客户端关闭时,服务器可以捕捉到。

 

一点总结:

服务器端需要两个套接字  监听套接字和已连接套接字两种类型套接字
                               监听套接字:主要用来接受三次握手数据,一旦三次握手完成,就将他放到已连接队列,accept就可以从队列中返回一个连接。返回的连接,我们称之为已连接套接字,他主要是用 来通信的,并不能接受连接。他是一个主动套接字。
客户端只有一种套接字 已连接套接字

posted @ 2016-12-09 15:36  ren_zhg1992  阅读(116)  评论(0)    收藏  举报