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