zoukankan      html  css  js  c++  java
  • 服务端socket重用属性设置

    初始化socket

    socket是一种系统资源,并不是每次初始化都一定成功,因此为了避免初始化失败,一般使用多次初始化的方式,如下所示:

    unsigned int times = 0x0;
    while((server = socket(PF_INET6, SOCK_STREAM, 0)) < 0 && times < 0x3)
    {       
        times++;
        printf("create socket faild %d times
    ", times);
    }

    设置socket的可重用属性

    一般来说,一个端口释放后需要等待两分钟左右才能被再次使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。例如:一个进程监听一个端口,进程在某个时候异常重启了,如果socket没有设置可重用属性,那么进程重启后就会出现bind错误,因为同一个端口两次监听的时间间隔需要75秒;如果设置了可重用属性,那么同一个端口释放之后马上就可以再次使用,如下所示:

    int reuse = 0x0;
    result = setsockopt(server, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(int));
    if(result != 0)
    {
        printf(" Fail to set socket reuseraddr options, errno = 0x%x!
    ", result);
    }

    设置socket的保活属性

    TCP是一种面向连接的协议,因此需要实时检查连接是否正常,特别是在上层长时间无数据传输时。一般检查连接是否正常的方式是提供一个保活机制,即定时的发送一种探测报文,可以是上层自己发送,也可以使用socket自己提供的保活机制,也就是保活属性,但是socket自己提供的保活机制一般不可靠,或者说是不完全可靠,因此在实际编程中是两种同时使用。首先上层启用一个任务,定时发送检测报文,然后设置socket的保活属性,以期达到完全可靠,如下所示:

    int optval = 0x1;
    result = setsockopt(server, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, sizeof(int));
    if(result != 0)
    {
        printf("Fail to set socket keepalive options, errno = 0x%x!
    ", result);
    }

    设置socket的缓冲区大小属性

    调用send和recv接口操作数据时,是直接将数据拷贝到缓冲区或是从缓冲器拷贝数据,如果缓存区满了,溢出后就会丢失数据,因此合理的设置缓存区的大小很重要,注意发送缓冲区和接收缓冲区是两个缓存区,不是同一个。如下所示:

    int BufSize = 32 * 1024;
    result = setsockopt(iSocketServer, SOL_SOCKET, SO_SNDBUF, (CHAR *)&BufSize, sizeof(BufSize));
    if(result!= 0)
    {
        printf("Fail to set socket sendbuf options, errno = 0x%x!
    ", result);
    }
    
    int BufSize = 32 * 1024;
    result = setsockopt(iSocketServer, SOL_SOCKET, SO_RCVBUF, (CHAR *)&BufSize, sizeof(BufSize));
    if(result != 0)
    {
        printf("Fail to set socket recvbuf options, errno = 0x%x!
    ", result);
    }

    绑定socket

    如果socket作为服务端,那么必须的操作是绑定一个IP,且绑定时并不是一定就会成功,因此使用多次绑定的方法,如下所示:

    struct sockaddr_in6 ServerIp_V6 = {0x0};
    
    memset(&ServerIp_V6, 0x0, sizeof(struct sockaddr_in6));
    ServerIp_V6.sin6_family = AF_INET6;
    ServerIp_V6.sin6_port   = htons(9999);
    ServerIp_V6.sin6_addr   = in6addr_any;
    
    times = 0;
    while((result = bind(server, (struct sockaddr *)&ServerIp_V6, sizeof(struct sockaddr_in6))) != 0 && times < 0x3)
    {
        times++;
        printf("Bind socket faild %d times
    ", times);    
    }

     监听socket

    同绑定socket的操作,注意最大监听个数的选择,如下所示:

    times = 0;
    listenMax = 0x10;
    while ((lResultCode = listen(server, listenMax)) != 0 && times < 0x3)
    {
        times++;
        printf("[tkQxPotnDaemon] listen socket faild %d times
    ", times);
    }

    等待客户端连接

    服务端一切设置好之后,就需要调用accpet阻塞式等待客户端来连接,如下所示:

    struct sockaddr_in6 ManagerIp_v6 = {0x0};
    int AddrLen = sizeof(struct sockaddr_in6);
    while(1)
    {
        memset(&ManagerIp_v6, 0x0, sizeof(struct sockaddr_in6));
        AcceptSocket = accept(server, (struct sockaddr *)&ManagerIp_v6, (socklen_t *)&AddrLen);
        if (AcceptSocket < 0)
        {
            continue;
        }
        
        // 注意:ip是ManagerIp_v6.sin6_addr中的12、13、14、15位
        // 如:ManagerIp_v6.sin6_addr[12]、ManagerIp_v6.sin6_addr[13]、ManagerIp_v6.sin6_addr[14]、ManagerIp_v6.sin6_addr[15]
        
        // 注意:端口号是htons(ManagerIp_v6.sin6_port)
        
        // 进行数据通信
    }
  • 相关阅读:
    golang 数据结构 优先队列(堆)
    leetcode刷题笔记5210题 球会落何处
    leetcode刷题笔记5638题 吃苹果的最大数目
    leetcode刷题笔记5637题 判断字符串的两半是否相似
    剑指 Offer 28. 对称的二叉树
    剑指 Offer 27. 二叉树的镜像
    剑指 Offer 26. 树的子结构
    剑指 Offer 25. 合并两个排序的链表
    剑指 Offer 24. 反转链表
    剑指 Offer 22. 链表中倒数第k个节点
  • 原文地址:https://www.cnblogs.com/chusiyong/p/12148043.html
Copyright © 2011-2022 走看看