測試Linux下tcp最大連接數限制


現在做服務器開發不加上高並發根本沒臉出門,所以為了以后吹水被別人懟“天天提高並發,你自己實現的最高並發是多少”的時候能義正言辭的懟回去,趁着元旦在家沒事決定自己寫個demo搞一搞。

這個測試主要是想搞明白Linux下哪些參數配置限制了連接數的最大值,上限是多少。

一、先說下demo的思路:

服務端用epoll實現,就是簡簡單單的接收連接,然后客戶端用go的goroutine,每個goroutine就是簡單的建立連接,然后什么也不做。

上代碼:

server:g++ -o test_epoll ./test_epoll.c     && ./test_epoll

/*
 * g++ -o test_epoll ./test_epoll.c
 */
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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

int SetReuseAddr(int fd)
{
    int optval = 1;
    socklen_t optlen = sizeof(optval);
    return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen);
}

int main()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    int iRet = SetReuseAddr(fd);
    if (iRet != 0)
    {
        printf("setsockopt for SO_REUSEADDR failed, error:%s\n", strerror(iRet));
        return iRet;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;
    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr))  == -1)
    {
        printf("bind failed, error:%s\n", strerror(errno));
        return errno;
    }

    if (listen(fd, 5) == -1)
    {
        printf("listen failed, error:%s\n", strerror(errno));
        return errno;
    }
    printf("Listening on 8080...\n");

    int epfd = epoll_create(102400);
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);

    struct epoll_event revents[102400];
    int iOnline = 0;
    while (1)
    {
        int num = epoll_wait(epfd, revents, 102400, 60 * 1000);
        printf("epoll_wait return %d\n", num);
        if (num > 0)
        {
            for (int i = 0; i < num; i++)
            {
                if (revents[i].data.fd == fd)
                {
                    int client;
                    struct sockaddr_in cli_addr;
                    socklen_t cli_addr_len = sizeof(cli_addr);
                    client = accept(fd, (struct sockaddr*)&cli_addr, &cli_addr_len);
                    if (client == -1)
                    {
                        printf("accept failed, error:%s\n", strerror(errno));
                        if (errno == EMFILE)
                        {
                            printf("per-process limit reached\n");
                            exit(errno);
                        }
                        if (errno == ENFILE)
                        {
                            printf("system-wide limit reached\n");
                            exit(errno);
                        }
                        continue;
                    }

                    iOnline++;
                    printf("Receive a new connection from %s:%d\n", inet_ntoa(cli_addr.sin_addr), cli_addr.sin_port);
                    event.events = EPOLLIN;
                    event.data.fd = client;
                    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
                }
            }
        }
        printf("Online number:%d\n", iOnline);
    }

    return 0;
}

client:go run client.go

package main

import (
    "net"
    "fmt"
    "time"
    "strconv"
    "runtime"
)

func Connect(host string, port int) {
    _, err := net.Dial("tcp", host+":"+strconv.Itoa(port))
    if err != nil {
        fmt.Printf("Dial to %s:%d failed\n", host, port)
        return
    }

    for {
        time.Sleep(30 * 1000 * time.Millisecond)
    }
}

func main() {
    count := 0
    for {
        go Connect("192.168.63.128", 8080)
        count++;
        fmt.Printf("Gorutue num:%d\n", runtime.NumGoroutine())
        time.Sleep(100 * time.Millisecond)
    }
}

二、開始測試

第一次:

先說結果,連接數達到1031時accept失敗了,當時還沒有對errno做判斷,所以只打印輸出了accept失敗。

然后首先想到的是ulimit -n的限制,查看了一下,默認值1024,然后就是修改這個值,在/etc/security/limits.conf中添加一下內容:

1 *    soft    nofile 102400
2 *    hard    nofile 102400

然后關閉當前xshell連接,重新連接即生效,現在看ulimit -n就是102400了。

這兩行的意思就是將每個進程能打開的文件描述符個數的soft、hard限制調整為102400,

注:ulimit -n 102400也可以生效,但是這個修改是臨時的。

然后進行第二次測試。

第二次:

逗比了,其實連接數只有2000+,我之前還在奇怪為啥Windows的默認連接數能有這么高呢,原來有些連接已經斷了,但是因為我沒有做處理,所以以為還在呢,看來我得再安裝一個虛擬機了[二哈]

待繼續。。。

安裝虛擬機去,

時間:2017-12-31 00:09:00

虛擬機安裝好了,接着搞,

這次是真的超過10K了。

連接數還在增加,不知道能不能最終達到10萬呢,小小的期待ing

時間:2017-12-31 00:41:00,最終上限卡在28232,golang一直報dial失敗,由於忘了打印出具體錯誤信息了,所以無從知道為什么dial失敗,所以只能再跑一次T_T

 時間:2017-12-31 01:01:00,添加打印dial失敗的錯誤信息的,又跑了一遍,還是在28232時出現dial失敗,錯誤信息:

golang的標准庫文檔中么有對錯誤信息的解釋,從錯誤信息來看,是分配地址失敗,於是想是不是端口地址范圍限制了。

 查看了一下端口地址范圍,確認就是這個限制,由於端口地址是16位,所以,就算把這個端口地址范圍修改為1024--65535,也最多能開啟64521個連接,而我現在只有一台虛擬機作為客戶端,所以想要實現10萬連接是不可能了,但是通過這次測試,也讓我搞明白了,到底哪些參數會限制連接的上限,這就是我想要的。

最后,感謝Linux內核團隊的大神們推出了epoll這么牛逼的機制,才使得我們現在想實現高並發是如此的容易,希望自己有一天也能這么牛逼,哈哈。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM