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;
}

时间: 2024-10-07 03:19:50

Socket编程实践(4)的相关文章

Socket编程实践(10) --select的限制与poll的使用

select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或者使用setrlimit函数设置,但一个系统所能打开的最大数也是有限的,跟内存大小有关,可以通过cat /proc/sys/fs/file-max 查看 /**示例: getrlimit/setrlimit获取/设置进程打开文件数目**/ int main() { struct rlimit rl;

Socket编程实践(19) --Socket API封装(2)

注:这一片博客与下一篇博客<Socket编程实践(20)>合为一篇,由于代码较多,所以分为两篇,本篇为上篇,主要讲解前一篇讲解的Socket类的增强,下一篇主要讲解怎样使用这个增强版的Socket类(ServerSocket/ClientSocket类的实现与使用)! 思想来源: 1)http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html 2)http://blog.csdn.net/column/details/linux66.

Socket编程实践(20) --Socket API封装(3)

注:这一片博客与上一篇博客<Socket编程实践(19)>合为一篇,由于代码较多,所以分为两篇,本篇为下篇,这一篇主要讲解怎样使用上一篇开发的增强版的Socket类(ServerSocket/ClientSocket类的实现与使用)! 思想来源: 1)http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html 2)http://blog.csdn.net/column/details/linux66.html 3)http://blo

Socket编程实践(6) --TCPNotes服务器

僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); } 3) 假设多个客户端同一时候关闭, 问题描写叙述如以下两幅图所看到的: watermark/2/text/aHR0cDovL2Jsb2cuY

Socket编程实践(6) --TCP服务端注意事项

僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); } 3) 如果多个客户端同时关闭, 问题描述如下面两幅图所示: /** client端实现的测试代码**/ int main() { int s

Socket编程实践(6) --TCP粘包原因与解决

流协议与粘包 粘包的表现 Host A 发送数据给 Host B; 而Host B 接收数据的方式不确定 粘包产生的原因 说明 TCP 字节流,无边界 对等方,一次读操作,不能保证完全把消息读完 UDP 数据报,有边界 对方接受数据包的个数是不确定的 产生粘包问题的原因分析 1.SQ_SNDBUF 套接字本身有缓冲区 (发送缓冲区.接受缓冲区) 2.tcp传送的端 mss大小限制 3.链路层也有MTU大小限制,如果数据包大于>MTU要在IP层进行分片,导致消息分割. 4.tcp的流量控制和拥塞控

Socket编程实践(7)   --TCP粘包解决方法2

包尾加\n编程实践 SYNOPSIS #include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t len, int flags); 与read相比,只能用于套接字文件描述符,而且多了一个flags Flags常用取值: MSG_OOB(紧急指针,带外数据) This flag requests receipt of out-of-band data that wo

Socket编程实践(12) --UDP编程基础

UDP特点 无连接,面向数据报(基于消息,不会粘包)的数据传输服务; 不可靠(可能会丢包, 乱序, 重复), 但因此一般情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #include <sys/types.h> #include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *a

Socket编程实践(2)

Socket API基本编程模型 TCP客户/服务器模型 简单echo服务器模型 Socket 基础API实践 socket函数 #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> 功能:创建一个套接字用于通信 原型 int socket(int domain, int type, int protocol); 参数 domain:指定通信协议族(protocol family),常用取值AF_INET(IP

Socket编程实践(3)

Socket 基础API实践(2)--实现回射服务器 accept函数 功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞. 原型 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 参数 sockfd:服务器套接字 addr:将返回对等方的套接字地址 addrlen:返回对等方的套接字地址长度 返回值 On  success, these system calls return a nonneg