c10k测试:每连接新线程回显服务器…

      一直听说每连接开线程的服务器当连接多了以后,由于线程切换和线程竞争导致的开销将会导致每秒处理请求数快速下降。最近写了个每连接开线程的回显服务器想要验证一下这种说法。一开始以为会画出一张漂亮的图(像java nio vs tomcat那张图一样的)。结果却让我吃了一惊:连接数增加到了15000,服务器威猛依然,每秒处理请求数相比连接不多时没有明显下降( < 2%)。
      再次深感高性能这块不能拍脑袋想当然。之所以出现这样的结果。与测试程序很有关系。由于回显服务器线程间不需要访问共同的数据,所以不会发生线程竞争。至于线程切换,这些线程大部分时间都堵塞在IO上,很少时间正在卖力干活。操作系统还不至于用堵塞在IO上的线程替换掉正在卖力干活的线程。
      上代码前说两句:
      1)因为用堵塞IO,没有用asio,因为asio在堵塞IO方面做得不好,而且阻塞IO本身就很简单了。也不想再引入啥封装。所以代码里面有挺多难看的条件编译语句,将就看着吧。
      2)在windows xp和linux rs5主机上测试通过。性能100连接-15000连接时差不多。测试时每个packet(不含tcp头ip头)=100B, 大概9w packet/s。基本把进出带宽都跑满了。
      3)可能会有人奇怪,为啥tcp还有packet的概念,这是逻辑packet。反正客户端就每次发送固定长度的内容,服务器接收完固定长度内容才发送给客户端。对于回显服务器来说这是没必要的,收多少发多少就好了。但大部分tcp服务器一般都是要收完一个逻辑packet才开始处理。

=======================代码的分割线==================================
#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>

#ifndef _WIN32
#include <termios.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#else
#include <conio.h>
#include <winsock.h>
#pragma comment(lib, "Ws2_32.lib")
typedef unsigned long int in_addr_t;
#endif

using namespace std;

int packet_size = 0;
boost::mutex cout_mtx;

void session(int clientfd)
{
      enum {BUF_SIZE = 4096};
      char buf[4096];
      int recv_times = 0;
      while (true)
      {
            int left_len = packet_size;
            int offset = 0;
            while (left_len > 0)
            {
                  int recv_size = recv(clientfd, (buf + offset), left_len, 0);
                  if (recv_size <= 0)
                  {
                        break;
                  }
                  else
                  {
                        left_len -= recv_size;
                        offset += recv_size;
                  }
            }
            if (left_len != 0)
            {
                  break;
            }
            ++ recv_times;
            if (send(clientfd, buf, packet_size, 0) != packet_size)
                           
                  break;
                     
      }
#ifndef _WIN32
      close(clientfd);
#else
      closesocket(clientfd);
#endif
      {
            boost::mutex::scoped_lock lock(cout_mtx);
            cout << recv_times << endl;
      }
}

int main(int argc, char* argv[])
{
#ifdef _WIN32
      WORD wVersionRequested = MAKEWORD(2,2);
      WSADATA wsaData;     
      if (WSAStartup(wVersionRequested,&wsaData) != 0)
      {
            cerr << "Unable to initialize the Winsock library!" << endl;
            return 1;
      }
#endif

      try
      {
            if (argc != 3)
            {
                  cerr << "Usage: thread_per_conn_server <port> <packet_size>" << endl;
                  return 1;
            }
            unsigned short port = boost::lexical_cast<unsigned short>(argv[1]);
            packet_size = boost::lexical_cast<int>(argv[2]);
            if (packet_size <= 0)
            {
                  cerr << "packet_size should be larger than 0!" << endl;
                  return 1;
            }

            int sockfd;
            struct sockaddr_in address;
            if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            {
                  cerr << "Unable to open socket!" << endl;
                  return 1;
            }
            const int reuse_flag = 1;
            if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_flag, sizeof(int)) == -1)
            {
                  cerr << "Unable to set reuseable socket binding property!" << endl;
                  return 1;                 
            }
            memset(&address, 0, sizeof(sockaddr_in));
            address.sin_family = AF_INET;
            address.sin_addr.s_addr = htonl(INADDR_ANY);
            address.sin_port = htons((unsigned short)port);
            if (bind(sockfd, (struct sockaddr *)&address, sizeof(struct sockaddr)) < 0)
            {
                  cerr << "Unable to bind socket!" << endl;
                  return 1;                 
                     
            if (listen(sockfd, 10) < 0)
            {
                  cerr << "Unable to set socket for listen mode!" << endl;
                  return 1;
                     
            while (true)
            {
                  struct sockaddr_in client_address;
#ifdef _WIN32
                  int client_address_size = sizeof(struct sockaddr_in);
#else
                  socklen_t client_address_size = sizeof(struct sockaddr_in);
#endif
                  int clientfd = accept(sockfd, (struct sockaddr *)&client_address, &client_address_size);
                  if (clientfd < 0)
                  {
                        cerr << "accept failed!" << endl;
                        return 1;
                  }
                  boost::thread t(boost::bind(&session, clientfd));
            }
           
#ifndef _WIN32
            close(sockfd);
#else
            closesocket(sockfd);
#endif
      }
      catch (std::exception& e)
      {
            cerr << "exception: " << e.what() << endl;
            return 1;
      }

#ifdef _WIN32
      WSACleanup();
#endif
      return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值