一、socket流程
socket 是面向客户/服务器模型而设计的,
针对客户和服务器程序提供不同的socket 系统调用
二、长连接和短连接
长连接:在多次请求中保持连接,使用同一个连接处理多次请求,直至出现错误或者异常才断开,并重新建立新的连接。
一般通过服务器端的长时间的读超时和客户端重用连接来实现。
典型例子:ui->as as->bs
短连接:每个请求建立一个连接,请求处理完成,则断开连接。
一般服务器端使用短的读超时。
典型例子:browser->apache client->LDC
ependingpoll同时支持长连接和短链接
三、常见问题
1.socket不够
大压力短连接,出现大量close_wait
解决方法:
lg.l_onoff = 1;
lg.l_linger = 0;
setsockopt(svrsock->sock, SOL_SOCKET, SO_LINGER, (const char*)&lg, sizeof(lg));
2.长连接请求混乱
长连接请求错乱,收到其他线程的请求
导致原因:在长连接出错的情况下,并没有关闭连接
3.SIGPIPE信号
向断开(半关闭)的连接中write数据时产生
通常处理方式:
signal(SIGPIPE, SIG_IGN);
SIGPIPE信号被忽略
4.TCP_NODELAY
TCP_NODELAY 不使用Nagle算法,不会将小包进行拼接成大包再进行发送,直接将小包发送出去,会使得小包时候用户体验非常好。
如果没有TCP_NODELAY在压力的情况下,会有延时(40ms)
Ependingpool自己默认的accept函数没有将socket设置成TCP_NODELAY.需要自己写回调函数来控制。
加入如下语句
client = ul_accept(sock, (struct sockaddr *)&sin, &slen);
if (client > 0)
setsockopt(client, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
5.SO_REUSEADDR
通常用于服务监听套接字,支持服务快速重启
如:
// 地址复用
int on = 1;
setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
6.一次读写不完全
正常情况下,read和write可能读写比指定数量少的数据
原因可能是:
内核缓冲区满
被信号中断
解决方法:
反复调用直到数据被全部读(写)完(注意死循环)
7.网络字节序转换
big-endian和little-endian
相关函数:
htonl、htons、ntohl、ntohs
保证程序兼容性和可移植性
8.select中的bug
不能操作多于1024个的socket
FD_SETSIZE在内核中定义为1024,并使用其声明最大的描述字集大小
解决方法:
调整程序,使其所需socket少于1024
使用poll()来代替select()
9.带超时的select
将要读的套接字加入rset中
调用select()。如果返回0则超时,返回-1则出错,否则利用FD_ISSET宏检查rset中的该套接字是否置位,如果是,则该套接字有数据可读,调用read()来读取
写超时控制和读超时控制的操作类似,但设置的是wret
10.带超时的Connect
设置套接字为非阻塞:
flags=fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL,flags|O_NONBLOCK);
调用connect()。如果返回成功则连接已经建立。不成功则检查errno,如果errno为EINPROGRESS,表示连接正在试图建立中。其他错误则应返回出错。
将套接字加入rset和wset,调用select()。返回0则表示超时。
如果select()返回成功,则检查sockfd的状态,仅可写则为连接建立成功,可读且可写表示出错。
恢复套接字的原有状态:
fcntl(sockfd,F_SETFL,flags);
11.调用被信号中断
大多数的阻塞系统调用都可能被信号中断
read()、write()、accept()、select()、connect()……
恢复被中断的系统调用
设置信号标志为SA_RESTART(并不是所有系统都支持;并不是所有系统调用都支持)
判断errno为EINTR,则再次调用函数(推荐)
read()、write()、select()、accept()可以重新调用一次:
do {
result = read(sockfd,buf,len);
}while ((result == -1) && (errno == EINTR));
connect()被中断不能重新调用,只能使用select()来等待连接完成。