socket效率到底如何

两台服务器用千兆交换机连接,带宽为1000Mbps,socket的效率到底如何?若server尽量快向client写数据,client收到数据后就丢弃,是否能占满千兆带宽?

测试发现和每次发送的包大小有关系,TCP包为1000bits(125字节)以上就能占满带宽:


因此,若client足够多而且都在请求数据,但是带宽上不去,就是服务器程序的问题了。

协议本身会降低带宽,使用rtmp协议的server一个进程带宽上限估计在260Mbps(若使用阻塞模式只能到150Mbps左右),能服务570个客户端(码流为500kbps),或者能服务870个客户端(300kbps),再有新的client过来会有延时就不行了。不过使用多进程,这台server还可以多启动进程,这样就可以将带宽占满,或者和FMS一样服务其他的vhost。

rtmp默认的chunk size为128,若使用这个尺寸发包,server只能到260Mbps(见上)。但将ChunkSize设为1100以上(和表中一样),带宽就能到945Mbps,能占满千兆网所有带宽。(rtmp server对rtmp client或tcp client都能占满带宽)

估计rtmp协议的效率如下,假设客户端的缓冲区设为10秒:
1. 选择视频为11Mbps时,只需要100个client即可跑满1000Mbps带宽,实际430Mbps(估计Server效率不行)。CPU很低只有14%。vmstat显示cs在12459,in为14439,比较高。内存使用1.8G。首次加载时间稍稍变慢需要2秒左右,seek后加载速度明显变慢需要10秒。预计CPU在18%左右。
2. 选择视频为900Kbps时,需要1000个客户端跑满1000Mbps带宽。实际上只能跑到300Mbps。CPU很低只有4%。vmstat显示cs在11360,in为1943,比较高。内存使用165M。首次加载时间和seek时间不变。预计CPU在72%左右。
3. 实际上单个进程不可能跑满945Mbps带宽,那个时候client的延迟已经很大了。2000个300Kbps的客户端,跑到500Mbps左右,单个进程,保持每个client都不延迟,已经是很好的设计了。支持高并发,必须要单线程和异步,使用多线程会显著增加开销(非线性)。
4. printf打日志对系统负载有影响,若打出的日志很多,会有影响。程序保持高性能,就是不block同时尽量不做和效率无关的事情。

测试程序如下:

// simple-server: send data as fast as possible.
#include 
#include 
#include 
#include 
using namespace std;

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define err_exit(msg) cout << "[error] " << msg << endl; exit(1)

struct UserOptions{
    int port;
    int packet_size;
};

void discovery_user_options(int argc, char** argv, UserOptions& options){
    if(argc <= 2){
        cout << "Usage: " << argv[0] << "  " << endl
            << "port: the port to listen" << endl
            << "packet_size: the packet size in b. must >= 8" << endl
            << "For example: " << argv[0] << " 1990 1000" << endl;
        exit(1);
    }
    
    options.port = atoi(argv[1]);
    options.packet_size = atoi(argv[2]);
    assert(options.port > 0);
    assert(options.packet_size >= 8);
}

int listen_server_socket(UserOptions& options){
    int serverfd = socket(AF_INET, SOCK_STREAM, 0);
    
    if(serverfd == -1){
        err_exit("init socket error!");
    }
    cout << "init socket success! #" << serverfd << endl;
    
    int reuse_socket = 1;
    if(setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1){
        err_exit("setsockopt reuse-addr error!");
    }
    cout << "setsockopt reuse-addr success!" << endl;
    
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(options.port);
    addr.sin_addr.s_addr = INADDR_ANY;
    if(bind(serverfd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1){
        err_exit("bind socket error!");
    }
    cout << "bind socket success!" << endl;
    
    if(listen(serverfd, 10) == -1){
        err_exit("listen socket error!");
    }
    cout << "listen socket success! " << options.port << endl;
    
    return serverfd;
}

int main(int argc, char** argv){
    UserOptions options;
    discovery_user_options(argc, argv, options);
    int serverfd = listen_server_socket(options);
    
    while(true){
        int client = accept(serverfd, NULL, 0);
        alarm(60);
        cout << "get a client #" << client << ", packet size=" << options.packet_size << "b" << endl;
        if(client == -1){
            err_exit("accept client socket error!");
        }
        
        int size = options.packet_size / 8;
        char* data = new char[size];
        while(true){
            if(send(client, data, size, 0) == -1){
                close(client);
                cout <<  "send client error!" << endl;
                break;
            }
        }
    }
    
    return -1;
}

// simple-client recv data and delete it.
#include 
#include 
#include 
#include 
using namespace std;

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define err_exit(msg) cout << "[error] " << msg << endl; exit(1)

struct UserOptions{
    char* server_ip;
    int port;
};

void discovery_user_options(int argc, char** argv, UserOptions& options){
    if(argc <= 2){
        cout << "Usage: " << argv[0] << "  " << endl
            << "server_ip: the ip address of server" << endl
            << "port: the port to connect at" << endl
            << "For example: " << argv[0] << " 192.168.100.145 1990" << endl;
        exit(1);
    }
    
    options.server_ip = argv[1];
    options.port = atoi(argv[2]);
    assert(options.port > 0);
}

int connect_server_socket(UserOptions& options){
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    
    if(clientfd == -1){
        err_exit("init socket error!");
    }
    cout << "init socket success!" << endl;
    
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(options.port);
    addr.sin_addr.s_addr = inet_addr(options.server_ip);
    if(connect(clientfd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1){
        err_exit("connect socket error!");
    }
    cout << "connect socket success!" << endl;
    
    return clientfd;
}

int main(int argc, char** argv){
    UserOptions options;
    discovery_user_options(argc, argv, options);
    
    int client = connect_server_socket(options);
    int size = 100 /*kbps*/ * 1000 / 8;
    char* buf = new char[size];
    while(true){
        if(recv(client, buf, size, 0) <= 0){
            close(client);
            err_exit("client recv error!");
        }
    }

    return 0;
}


你可能感兴趣的:(socket效率到底如何)