天天看點

我個人的Linux TCP server和client測試源碼,C語言(2)(★firecat推薦★)

二、echo源碼2如下,main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/resource.h>    /*setrlimit */
 
#define ECHO_SERVER_PORT        1883
#define LISTEN_BACKLOG          16
#define MAX_EVENT_COUNT         32
#define BUF_SIZE                2048
#define MAXCONN                 60000
 
int set_fdlimit()
{
    //設定每個程序允許打開的最大檔案數
    //這項功能等價于linux終端指令 "ulimit -n 102400"
    struct rlimit rt;
    rt.rlim_max = rt.rlim_cur = MAXCONN;
    if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
    {
        perror("setrlimit error");
        return -1;
    }
 
    return 0;
}
 
int main() {
 
    //設定每個程序允許打開的最大檔案數,socket
    if (set_fdlimit() < 0)
    {
        return -1;
    }
 
    int ret, i;
    int server_fd, client_fd, epoll_fd;
    int ready_count;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    socklen_t addr_len;
    struct epoll_event event;
    struct epoll_event* event_array;
    char* buf;
 
    event_array = (struct epoll_event*)
                    malloc(sizeof(struct epoll_event)*MAX_EVENT_COUNT);
    buf = (char*)malloc(sizeof(char)*BUF_SIZE);
 
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(ECHO_SERVER_PORT);
 
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(server_fd == -1) {
        perror("create socket failed.\n");
        return 1;
    }
 
    //一個端口釋放後會等待兩分鐘之後才能再被使用,SO_REUSEADDR是讓端口釋放後立即就可以被再次使用。
    int reuse_addr = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1)
    {
        return -1;
    }
 
    ret = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if(ret == -1) {
        perror("bind failed.\n");
        return 1;
    }
 
    ret = listen(server_fd, LISTEN_BACKLOG);
    if(ret == -1) {
        perror("listen failed.\n");
        return 1;
    }
 
    fprintf(stderr,"listen on,fd=%d\n",server_fd);
 
    epoll_fd = epoll_create(1);
    if(epoll_fd == -1) {
        perror("epoll_create failed.\n");
        return 1;
    }
    event.events = EPOLLIN;
    event.data.fd = server_fd;
    ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);
    if(ret == -1) {
        perror("epoll_ctl failed.\n");
        return 1;
    }
 
    while(1) {
        ready_count = epoll_wait(epoll_fd, event_array, MAX_EVENT_COUNT, -1);
        if(ready_count == -1) {
            perror("epoll_wait failed.\n");
            return 1;
        }
        for(i = 0; i < ready_count; i++) {
            if(event_array[i].data.fd == server_fd) {
                client_fd = accept(server_fd,
                            (struct sockaddr*)&client_addr, &addr_len);
                if(client_fd == -1) {
                    perror("accept failed.\n");
                    return 1;
                }
                event.events = EPOLLIN;
                event.data.fd = client_fd;
                ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
                if(ret == -1) {
                    perror("epoll_ctl failed.\n");
                    return 1;
                }
 
                fprintf(stderr,"accept,fd=%d\n",client_fd);
            }
            else {
                ret = recv(event_array[i].data.fd, buf, BUF_SIZE, 0);
                if(ret <= 0) {
                    close(event_array[i].data.fd);
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL,
                              event_array[i].data.fd, &event);
                    continue;
                }
                ret = send(event_array[i].data.fd, buf, (size_t)ret, 0);
                if(ret == -1) {
                    perror("send failed.\n");
                }
            }
        } // for each event
    } // while(1)
 
    close(epoll_fd);
    close(server_fd);
    free(event_array);
    free(buf);
 
    return 0;
}

      

三、用戶端測試

1、工具介紹

強大的TcpServer壓力測試工具源碼(附突破連接配接限制的方法和工具)

TCP_UDP_PerformanceTest

TCPCOPY:

https://github.com/session-replay-tools/tcpcopy

Apache Bench:

https://httpd.apache.org/docs/2.4/programs/ab.html

-- ab

Apache jmeter:

https://github.com/apache/jmeter

webbench:

http://home.tiscali.cz/~cz210552/webbench.html

★Windows解決端口号限制方法,修改兩個系統資料庫: (必須修改,否則測試工具作為用戶端發起TCP連接配接,數目上不去)

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort 如果沒有,則手動建立 DWord(32位) ”數值資料“改為十進制65534 或者認為适當的值。 此值表示 使用者最大使用的端口數量,預設為5000。

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters\TCPTimedWaitDelay 如果沒有,則手動建立 DWord(32位) ”數值資料“改為十進制30 或者你認為适當的值。 此值表示一個關閉後的端口等待多久之後可以重新使用,預設為120秒,也就是2分鐘才可以重新使用。

2、實踐體會

我個人的Linux TCP server和client測試源碼,C語言(2)(★firecat推薦★)

★Server Test Tool這個工具,測試完成需要按“Stop“,界面會暫時卡死,沒關系,請耐心等待,千萬不要強行kill程序,否則會導緻資源無法正常釋放。如果資源無法正常釋放,會出現連接配接數上不去的情況,此時隻能通過重新開機電腦可以解決。

我在公司辦公電腦,使用如圖配置,每1000ms發起1000個tcp連接配接,測試通過,可以達到ConnectTotal的數值;

但是我在家裡電腦,使用如圖配置,tcp連接配接數卻怎麼都上不去,始終不過萬,後來我調整為OnHeartbeat模式,每1000ms發起100個tcp連接配接,方可達成。因為OnHeartbeat比OnTimer更省流量,另外發起連接配接數的頻次也降下來了。看來參數很重要,與路由器帶寬等的承受能力有關。不妨多嘗試,找出成本效益最好的參數組合。

★如果使用SSH遠端通路的方式,伺服器程式不能是控制台帶列印資訊的方式,否則用戶端連接配接會出問題,一來TCP用戶端連接配接數很難上的去,二來SSH軟體網絡很容易中斷。結論,伺服器程式應該使用守護程序,在背景運作。

我個人的Linux TCP server和client測試源碼,C語言(2)(★firecat推薦★)
我個人的Linux TCP server和client測試源碼,C語言(2)(★firecat推薦★)