套接字乒乓球表演

时间:2010-05-14 14:59:15

标签: windows performance sockets

我写了两个简单的程序(在C ++和C#中试过)。

这是伪代码:

--------客户端---------------

for(int i = 0; i < 200.000; i++)  
{  
    socket_send("ping")  
    socket_receive(buff)  
}  

---------服务器-------------

while(1)  
{  
    socket_receive(buff)  
    socket_send("pong")  
}  

我在Windows上尝试过。

客户的执行时间约为45秒。有人可以解释一下为什么这么长时间吗? 据我所知,如果客户端和服务器之间存在真正的网络连接,那么一个'乒乓'的时间就是: generate_ping + send_via_network + generate_pong + send_via_network 但这里一切都是在“本地”模式下完成的。

有没有办法使用网络套接字使这个进程间乒乓更快(我不是在询问共享内存,例如:))

3 个答案:

答案 0 :(得分:2)

这可能是一个老鹰问题吗?您正在发送非常小的数据包,然后立即等待响应。 TCP堆栈会暂停数据,直到确定您不再发送任何数据为止。设置TCP_NODELAY选项可以使其更快。

令我对这个假设感到困惑的是,为什么没有其他人看到它以及为什么在linux上看不到相同的效果(我几乎一无所知)。我知道Windows上的TCP堆栈几乎肯定不会比linux上的堆栈慢9倍 - 有人会注意到。

答案 1 :(得分:1)

您的机器是单CPU吗?然后在每次发送后都需要完整的任务切换。 这需要时间。

即使您的计算机是多CPU,网络堆栈也必须使用锁/互斥锁来保证一次只有一个进程尝试更新某些内部数据结构。尝试通信的其他过程需要等待,例如,在旋转环中,用于释放互斥体。 这需要时间。(良好的网络堆栈实现将导致彼此独立的不同进程上的通信请求之间的干扰最小 - 但显然情况并非如此!)

此外,网络代码几乎肯定存在于操作系统内核中,这需要在进入和退出时切换CPU保护级别。 这需要时间。

(我假设你的意思是“200,000”,如“二十万”,而不是“200.000”,如“二百,小数位后的3位冗余精度”。我意识到{{1}的含义}和,在某些语言中交换了我的理解,只是确保。)

最后,延迟(这是你真正在这里测量的)对你来说真的很重要吗?我希望你会发现带宽很好 - 即你可以在不花费更多时间的情况下每次发送更多数据(当然我希望最多1个虚拟内存页面,通常大约4Kb)。 / p>

答案 2 :(得分:0)

这里是C#中生产者的粗略代码:

using System;
using System.Net;
using System.Net.Sockets;


namespace Producer
{    
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("Usage:");
                Console.WriteLine("Producer.exe <server_ip> <port>");
                return;
            }
            string ipAddress = args[0];
            string port = args[1];

            Console.WriteLine("IpAddress: " + ipAddress);
            Console.WriteLine("Port     : " + port);

            Socket s = new Socket(AddressFamily.InterNetwork, 
                           SocketType.Stream, ProtocolType.Tcp);
            s.Connect(new IPEndPoint(IPAddress.Parse(ipAddress), 
                           int.Parse(port)));

            Console.WriteLine("Press key to start pinging");
            Console.ReadKey();

            byte[] buff = new byte[1];
            Console.WriteLine("Pinging started");
            DateTime start = DateTime.Now;

            for (int i = 0; i < 200000; i++)
            {
                s.Send(buff);
                s.Receive(buff, 1, SocketFlags.None);
            }
            DateTime end = DateTime.Now;
            Console.WriteLine("Pinging finished in: " 
                               + (end - start).TotalSeconds);

            Console.ReadKey();
        }
    }
}

这是C#中的服务器:

using System;
using System.Net;
using System.Net.Sockets;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Usage:");
                Console.WriteLine("Server.exe <port>");
                return;
            }

            string port = args[0];

            //IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
            //IPAddress ipAddress = ipHostInfo.AddressList[0];
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint m_LocalEndPoint = new IPEndPoint(ipAddress, 
                                                        int.Parse(port));

            try
            {
                // Create a TCP/IP socket.
                Socket s = new Socket(AddressFamily.InterNetwork, 
                               SocketType.Stream, ProtocolType.Tcp);
                s.Bind(m_LocalEndPoint);
                s.Listen(10);

                Console.WriteLine("IpAddress: " + ipAddress.ToString());
                Console.WriteLine("Port     : " + port);

                byte[] buff = new byte[1];

                Socket s2 = s.Accept();
                Console.WriteLine("connected");

                while (true)
                {
                    s2.Receive(buff, 1, SocketFlags.None);
                    s2.Send(buff);
                }
            }
            catch( Exception )
            {
            }
        }
    }
}

这是生产者的输出(在Windows上运行):

Producer.exe 127.0.0.1 667
IpAddress: 127.0.0.1
Port     : 667
Press key to start pinging
nPinging started
Pinging finished in: 46,617032

我使用winsock2(类似于下面的Linux套接字的代码)使用C ++编写的程序得到非常相似的结果

用C语言编写并在Linux上启动:

制片:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>

int main(int argc, char** argv)
{
  int sockfd, portno, n;
  struct sockaddr_in serv_addr;
  struct hostent *server;
  char buff[1];
  time_t start, end;
  int i;

  if (argc != 3)
  {
  printf("Usage:\n");
  printf("%s <server_ip> <port>\n", argv[0]);
  return;
  }

  char* ipAddress = argv[1];
  char* port = argv[2];

  printf("IpAddress: %s\n", ipAddress);
  printf("Port     : %s\n", port);

  portno = atoi(port);
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) 
  printf("error - socket open\n");

  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = inet_addr(ipAddress);
  serv_addr.sin_port = htons(portno);
  if(0 > connect(sockfd,&serv_addr,sizeof(serv_addr)))
printf("error - connect\n");

  start = time(0);
  printf("ping-pong started\n");
  for(i = 0; i < 200000; i++)
  {
write(sockfd,buff,1);
read(sockfd,buff,1);
  }
  end = time(0);
  printf("finished in %d secs\n", end - start);

  return 0;
}

和服务器:     #包括     #包括     #包括     #包括     #include

int main(int argc, char** argv)
{
  int sockfd, newsockfd, portno, clilen;
  char buffer[1];
  struct sockaddr_in serv_addr, cli_addr;
  int n;

  if (argc < 2)
  {
printf("Usage:\n");
printf("%s <port>\n", argv[0]);
return;
  }

  portno = atoi(argv[1]);
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) 
  printf("error - socket open\n");

  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(portno);


  if (0 > bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))) 
printf("error - bind\n");

  listen(sockfd,5);
  clilen = sizeof(cli_addr);
  printf("waiting for connection...\n");
  newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
  if (newsockfd < 0) 
printf("error - accept\n");

  printf("connected\n");

  while(1)
  {
read(newsockfd,buffer,1);
write(newsockfd,buffer,1);
  }  
  return 0;
}

Producer(Linux)的输出:

$ ./a.out 127.0.0.1 666
IpAddress: 127.0.0.1
Port     : 666
ping-pong started
finished in 5 secs

我的机器是Intel Core Duo

Windows上发生了什么?