C++网络编程小心得
网络当前总结
(乱入)split 字符串
template<typename Out>
void split(const std::string &s, char delim, Out result) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
*(result++) = item;
}
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, std::back_inserter(elems));
return elems;
}
socket 传输字节序问题
由于进行网络传输涉及到大端序小端序问题,基本上int类型都会涉及到本地字节序和网络字节序之间互相转换的问题,
网络通用api中只提供了32位和16位数字的转换函数,没有提供64位的long类型的转换函数,自己实现如下:
#define TYP_INIT 0
#define TYP_SMLE 1
#define TYP_BIGE 2
long htonll(long src) {
static int htonll_type = TYP_INIT;
union {
long x;
char c[8];
} u;
if (htonll_type == TYP_INIT) {
u.x = 0x01;
htonll_type = (u.c[0] == 0x01) ? TYP_BIGE : TYP_SMLE;
}
if (htonll_type == TYP_BIGE) {
return src;
}
u.x = src;
char tmp;
tmp = u.c[0]; u.c[0] = u.c[7]; u.c[7] = tmp;
tmp = u.c[1]; u.c[1] = u.c[6]; u.c[6] = tmp;
tmp = u.c[2]; u.c[2] = u.c[5]; u.c[5] = tmp;
tmp = u.c[3]; u.c[3] = u.c[4]; u.c[4] = tmp;
return u.x;
}
long ntohll(long src) {
int ntohll_type = TYP_INIT;
union {
long x;
char c[8];
} u;
if (ntohll_type == TYP_INIT) {
u.x = 0x11;
ntohll_type = (u.c[0] == 0x11) ? TYP_BIGE : TYP_SMLE;
}
if (ntohll_type == TYP_BIGE) {
return src;
}
u.x = src;
char tmp;
tmp = u.c[0]; u.c[0] = u.c[7]; u.c[7] = tmp;
tmp = u.c[1]; u.c[1] = u.c[6]; u.c[6] = tmp;
tmp = u.c[2]; u.c[2] = u.c[5]; u.c[5] = tmp;
tmp = u.c[3]; u.c[3] = u.c[4]; u.c[4] = tmp;
return u.x;
}
port占用问题
描述:server端会绑定一个端口始终进行监听,监听来自客户端的请求,当强制杀死server服务后再开启,有时候就会失败,显示端口占用。
原因:端口的关闭(调用close)需要有一定时间,time_wait的时间不固定,最长会两分钟左右,强制kill掉就不一定是多久才可以关闭啦。
解决方式:在bind之前调用setsockopt函数,将端口设为可重用。
代码:
const int on = 1;
ret = setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
HANDLE_RETURN_LESS_ZERO(ret, "set sockopt reuse");
网络优化小技巧
- 设置TCP_NODELAY禁用Nagle算法,使用TCP时默认会开启Nagle算法,该算法会对网络传输带宽进行一定的优化,当发送比较小的数据包时,
它会等待一段时间期望等待更多数据组成一个比较大的数据包来进行传输,这就会有一定的时间延迟,看资料大概延迟40ms,没测过具体时间,通过设置TCP_NODELAY可以禁用Nagle算法,
一般对实时性要求较高时需要禁用Nagle算法,UDP无此问题。详见[https://www.cnblogs.com/lidabo/p/3773117.html]
void setTcpNodelay(int fd) {
int enable = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&enable, sizeof(enable));
}
- 数据包尽量一次性send完成和recv完成,尽量不要分多次进行。
心得
-
epoll的触发方式有边缘触发和水平触发,可根据需要进行设置触发方式
网络上的讲解很玄乎,自己也没有看懂,自己测试理解如下:
边缘触发:当有状态发生变化时才会触发,拿recv并且触发事件为有数据可以读触发(EPOLLIN)举例,当前没有数据可以recv,当有数据可以recv时会触发epoll,这时如果再有数据过来则不会触发epoll,recv完成后,没有数据,当再有数据可以读时又会触发,数据持续不断的过来时基本只会触发一次,顾名思义,状态变化时才会触发。
水平触发:这个比较好理解,如果还有数据没有recv那就会不停的触发。(也拿上面的recv举例) -
socket在阻塞方式下send和recv的行为
socket默认是阻塞方式,send在send缓冲区满了后,或缓冲区长度小于send数据长度,那send就会一直阻塞直到send成功,返回send的长度。
recv同理,没有数据接收时,recv也会一直阻塞。 -
socket在非阻塞方式下send和recv的行为。
send和recv无论成功失败都是会立刻返回,send返回send的长度,recv返回recv的长度,如果返回0则代表对端已经断开连接,
如果返回<0,那基本上errno=EAGAIN,代表重新进行一次send或recv,返回别的值那基本上就是error。