网络编程——网络编程的注意

端口复用

比如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代表发送大小

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