顿悟,之前浪费了大把光阴。。痛惜!
决定落户到oschina了,支持开源。先贴上CSDN已发表过的。
测试目的:比较c,c++,node.js,erlang网络TCP读写性能
测试环境:linux
测试条件:Server为简单的TCP echo服务器,Server和Client运行在同一机器上,10000长连接,100活动连接
测试结果:
c++ boost::asio
connect=10000,active connect=100,req=148791,time=60,req/sec=2479.85,msec/req=40.343
erlang kernel-poll false
connect=10000,active connect=100,req=979803,time=60,req/sec=16330,msec/req=6.12356
node.js
connect=10000,active connect=100,req=1378370,time=60,req/sec=22972.8,msec/req=4.35543
c libevent
connect=10000,active connect=100,req=3719106,time=60,req/sec=61985.1,msec/req=1.61258
erlang kernel-poll true
connect=10000,active connect=100,req=6377574,time=60,req/sec=106293,msec/req=0.939882
源代码见下:
asio_echo_server.cpp
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
using boost::asio::ip::tcp;
int total_conn = 0;
class session
{
public:
session(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, this,
boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
start_accept();
}
private:
void start_accept()
{
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
void handle_accept(session* new_session,
const boost::system::error_code& error)
{
start_accept();
if (!error)
{
std::cout << "total connect =" << ++total_conn <<std::endl;
new_session->start();
}
else
{
delete new_session;
}
}
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[])
{
try
{
if (argc < 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server s(io_service, atoi(argv[1]));
int thread_num = 6;
if (argc > 2)
thread_num = atoi(argv[2]);
boost::thread_group th_group;
for (int i=0; i<thread_num; ++i)
{
th_group.add_thread(new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service)));
}
th_group.join_all();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
libevent-echo-server.c
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <event.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
int buf_len = 8192;
int msg_len = 4096;
int total = 0;
int setnonblock(int fd)
{
int flags;
flags = fcntl(fd, F_GETFL);
if (flags < 0)
return flags;
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0)
return -1;
return 0;
}
void connection_echo(int fd, short event, void *arg)
{
struct event *ev = (struct event *)arg;
event_add(ev, NULL);
char buf[buf_len];
int read_len = read(fd, buf, msg_len);
write(fd, buf, read_len);
}
void connection_accept(int fd, short event, void *arg)
{
/* for debugging */
fprintf(stderr, "%s(): fd = %d, event = %d, total = %d.\n", __func__, fd, event, ++total);
/* Accept a new connection. */
struct sockaddr_in s_in;
socklen_t len = sizeof(s_in);
int ns = accept(fd, (struct sockaddr *) &s_in, &len);
if (ns < 0) {
perror("accept");
return;
}
setnonblock(ns);
/* Install echo server. */
struct event *ev = (struct event *)malloc(sizeof(struct event));
event_set(ev, ns, EV_READ, connection_echo, ev);
event_add(ev, NULL);
}
int main(void)
{
/* Request socket. */
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("socket");
exit(1);
}
/* bind() */
struct sockaddr_in s_in;
memset(&s_in, 0, sizeof(s_in));
s_in.sin_family = AF_INET;
s_in.sin_port = htons(9000);
s_in.sin_addr.s_addr = INADDR_ANY;
if (bind(s, (struct sockaddr *) &s_in, sizeof(s_in)) < 0) {
perror("bind");
exit(1);
}
/* listen() */
if (listen(s, 1000) < 0) {
perror("listen");
exit(1);
}
/* Initial libevent. */
event_init();
/* Create event. */
struct event ev;
event_set(&ev, s, EV_READ | EV_PERSIST, connection_accept, &ev);
/* Add event. */
event_add(&ev, NULL);
event_dispatch();
return 0;
}
erlang-echo-server.erl
-module(echo_server).
-export([start/0]).
start() ->
{ok, Listen} = gen_tcp:listen(9000, [binary,
%{packet, 4},
{reuseaddr, true},
{backlog, 2000},
{active, true}]),
spawn(fun() -> par_connect(Listen, 0) end).
par_connect(Listen, Count) ->
{ok, Socket} = gen_tcp:accept(Listen),
New = Count + 1,
io:format("Accept succ ~p~n", [New]),
spawn(fun() -> par_connect(Listen, New) end),
loop(Socket).
loop(Socket) ->
receive
{tcp, Socket, Bin} ->
gen_tcp:send(Socket, Bin),
loop(Socket);
{tcp_closed, Socket} ->
io:format("Server socket closed~n")
end.
node-echo-server.js
var net = require('net');
var cnt = 0;
var server = net.createServer(function (socket) {
socket.pipe(socket);
});
server.listen(9000, "127.0.0.1");
console.log('Server running at 127.0.0.1:9000');