首页 > 代码库 > Socket编程实践(4)

Socket编程实践(4)

Socket API 中的地址复用

      服务器端尽可能使用SO_REUSEADDR,在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。该选项可以使得不必等待TIME_WAIT状态消失就可以重启服务器.

SYNOPSIS

       #include <sys/types.h>          /* See NOTES */

       #include <sys/socket.h>

       int getsockopt(int sockfd, int level, int optname,

                      void *optval, socklen_t *optlen);

       int setsockopt(int sockfd, int level, int optname,

                      const void *optval, socklen_t optlen);

 

可以在bind之前添加代码(完整代码请参照博文最后):

    int optval = 1;

    if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == -1)

    {

        err_exit("setsockopt SO_REUSEADDR error");

    }

用以支持地址复用.

 

Socket服务多并发--多客户端连接

 

    可以从上图看出,我们的服务器最大的缺点就是无法支持多客户连接,即使客户端能够连接到服务器上,服务器也不为该客户做服务,(直接没什么反应),虽然链接是有的(也就是说,客户端是已经连接到服务器上的了,但是服务器就是不搭理你....)

 



   从服务器的下面这段代码我们可以看出端倪....

 struct sockaddr_in peerAddr;
    socklen_t peerLen;
    //注意:一次只能处理一个连接
    int peerSockfd = accept(sockfd, (struct sockaddr *)&peerAddr,&peerLen);
    if (peerSockfd == -1)
    {
        err_exit("accept error");
    }

    //打印客户信息
    cout << "Client:" << endl;
    cout << "\tsin_port: " << ntohs(peerAddr.sin_port) << endl;
    cout << "\tsin_addr: " << inet_ntoa(peerAddr.sin_addr) << endl;
 cout << "\tsocket: " << peerSockfd << endl;
  
 //.....

即服务器运行一次,只能为一个客户端服务一次!!!!


并发服务器实现


1.客户端向服务器发送请求

 




 


2.典型的(多进程)并发服务器程序框架



//完整的server端代码
#include "commen.h"

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if (sockfd == -1)
    {
        err_exit("socket error");
    }

    int optval = 1;
    if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == -1)
    {
        err_exit("setsockopt SO_REUSEADDR error");
    }
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(8002);
    serverAddr.sin_addr.s_addr = INADDR_ANY;    //绑定本机的任意一个IP地址
    //serverAddr.sin_addr.s_addr = inet_addr("10.255.218.20");
    if (bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
    {
        err_exit("bind error");
    }

    //一旦调用了listen,则sockfd编程被动套接字:等待客户端的连接(只能接受连接,不能发送连接)
    if (listen(sockfd,SOMAXCONN) == -1)
    {
        err_exit("listen error");
    }

    struct sockaddr_in peerAddr;
    socklen_t peerLen;

    while (true)
    {
        int peerSockfd = accept(sockfd, (struct sockaddr *)&peerAddr,&peerLen);
        if (peerSockfd == -1)
        {
            err_exit("accept error");
        }

        //打印客户信息
        cout << "Client:" << endl;
        cout << "\tsin_port: " << ntohs(peerAddr.sin_port) << endl;
        cout << "\tsin_addr: " << inet_ntoa(peerAddr.sin_addr) << endl;
        cout << "\tsocket: " << peerSockfd << endl;

        //每有一个客户端连接进来,就fork一个子进程,
        //相应的业务处理由子进程完成,父进程继续监听
        pid_t pid = fork();
        if (pid == -1)
        {
            close(sockfd);
            close(peerSockfd);
            err_exit("fork error");
        }
        else if (pid == 0)  //子进程,处理业务
        {
            close(sockfd);  //子进程关闭监听套接字,因为子进程不负责监听任务
            char buf[BUFSIZ];
            memset(buf,0,sizeof(buf));
            ssize_t readCount = 0;
            while ((readCount = read(peerSockfd,buf,sizeof(buf))) >= 0)
            {
                //如果在读取数据的过程中,read返回0,则说明对方已经关闭连接
                if (readCount == 0)
                {
                    err_exit("read peerSockfd error");
                }
                if (write(peerSockfd,buf,readCount) == -1)
                {
                    err_exit("write peerSockfd error");
                }
                buf[readCount] = ‘\0‘;
                fputs(buf,stdout);
                memset(buf,0,sizeof(buf));
            }
        }
        else if (pid > 0)   //父进程
        {
            close(peerSockfd);  //父进程关闭连接套接字,因为父进程不负责为子进程服务
        }
    }

    close(sockfd);
    return 0;
}




Socket编程实践(4)