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