網絡編程——網絡編程的注意

端口複用

比如80端口,關閉後馬上又啓用一個80,爲了保證可以馬上使用,必須設置so_reuseaddr

網絡編程sigpipe問題

SIGPIPE產生的原因是這樣的:如果一個 socket 在接收到了 RST packet 之後,程序仍然向這個 socket 多次寫入數據,那麼就會產生SIGPIPE信號。
需要設置sigpipe
網絡編程中的 SIGPIPE 信號

tcp-Nagle算法

如果是類似write-write-read,過多的write會造成浪費。啓動TCP_NODELAY,就意味着禁用了Nagle算法,允許小包的發送。Nagle算法通過減少需要傳輸的數據包,來優化網絡。
需要設置nodelay
TCP連接中啓用和禁用TCP_NODELAY有什麼影響?

測試三個參數的程序

//客戶端
#include "InetAddress.h"
#include "TcpStream.h"

#include <string.h>
#include <sys/time.h>
#include <unistd.h>

double now()
{
  struct timeval tv = { 0, 0 };
  gettimeofday(&tv, NULL);
  return tv.tv_sec + tv.tv_usec / 1000000.0;
}

int main(int argc, char* argv[])
{
  if (argc < 3)
  {
    printf("Usage: %s [-b] [-D] [-n num] hostname message_length\n", argv[0]);
    printf("  -b Buffering request before sending.\n"
           "  -D Set TCP_NODELAY.\n"
           "  -n num Send num concurrent requests, default = 1.\n");
    return 0;
  }

  int opt = 0;
  bool buffering = false;
  bool tcpnodelay = false;
  int num = 1;
  while ( (opt = getopt(argc, argv, "bDn:")) != -1)
  {
    switch (opt)
    {
      case 'b':
        buffering = true;
        break;
      case 'D':
        tcpnodelay = true;
        break;
      case 'n':
        num = atoi(optarg);
        break;
      default:
        printf("Unknown option '%c'\n", opt);
        return 0;
    }
  }

  if (optind > argc - 2)
  {
    printf("Please specify hostname and message_length.\n");
    return 0;
  }

  const char* hostname = argv[optind];
  int len = atoi(argv[optind+1]);

  InetAddress addr(3210);
  if (!InetAddress::resolve(hostname, &addr))
  {
    printf("Unable to resolve %s\n", argv[1]);
    return 0;
  }

  printf("connecting to %s\n", addr.toIpPort().c_str());
  TcpStreamPtr stream(TcpStream::connect(addr));
  if (!stream)
  {
    printf("Unable to connect %s\n", addr.toIpPort().c_str());
    perror("");
    return 0;
  }

  if (tcpnodelay)
  {
    stream->setTcpNoDelay(true);
    printf("connected, set TCP_NODELAY\n");
  }
  else
  {
    stream->setTcpNoDelay(false);
    printf("connected\n");
  }


  double start = now();
  for (int n = 0; n < num; ++n)
  {
    printf("Request no. %d, sending %d bytes\n", n, len);
    if (buffering)
    {
      std::vector<char> message(len + sizeof len, 'S');
      memcpy(message.data(), &len, sizeof len);
      int nw = stream->sendAll(message.data(), message.size());
      printf("%.6f sent %d bytes\n", now(), nw);
    }
    else
    {
      stream->sendAll(&len, sizeof len);
      printf("%.6f sent header\n", now());
      usleep(1000); // prevent kernel merging TCP segments
      std::string payload(len, 'S');
      int nw = stream->sendAll(payload.data(), payload.size());
      printf("%.6f sent %d bytes\n", now(), nw);
    }
  }

  printf("Sent all %d requests, receiving responses.\n", num);
  for (int n = 0; n < num; ++n)
  {
    int ack = 0;
    int nr = stream->receiveAll(&ack, sizeof ack);
    printf("%.6f received %d bytes, ack = %d\n", now(), nr, ack);
  }
  printf("total %f seconds\n", now() - start);
}

//服務端
#include "Acceptor.h"
#include "InetAddress.h"
#include "TcpStream.h"

#include <thread>
#include <vector>

#include <assert.h>
#include <string.h>
#include <sys/time.h>

double now()
{
  struct timeval tv = { 0, 0 };
  gettimeofday(&tv, NULL);
  return tv.tv_sec + tv.tv_usec / 1000000.0;
}

// an interative request-response server
int main(int argc, char* argv[])
{
  InetAddress listenAddr(3210);
  Acceptor acceptor(listenAddr);
  printf("Accepting... Ctrl-C to exit\n");
  int count = 0;
  bool nodelay = argc > 1 && strcmp(argv[1], "-D") == 0;
  while (true)
  {
    TcpStreamPtr tcpStream = acceptor.accept();
    printf("accepted no. %d client\n", ++count);
    if (nodelay)
      tcpStream->setTcpNoDelay(true);

    while (true)
    {
      int len = 0;
      int nr = tcpStream->receiveAll(&len, sizeof len);
      if (nr <= 0)
        break;
      printf("%f received header %d bytes, len = %d\n", now(), nr, len);
      assert(nr == sizeof len);

      std::vector<char> payload(len);
      nr = tcpStream->receiveAll(payload.data(), len);
      printf("%f received payload %d bytes\n", now(), nr);
      assert(nr == len);
      int nw = tcpStream->sendAll(&len, sizeof len);
      assert(nw == sizeof len);
    }

    printf("no. %d client ended.\n", count);
  }
}

測試結果:
./nodelay_server
./nodelay -D 127.0.0.1 1000 //設置no_delay比delay快
//-b代表一次只發一個包,沒有-b代表先發一個長度在發內容包
//-D代表不允許小包發送
//-n代表發送大小

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章