一、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()来等待连接完成。