Socket 编程 小知识库
来源:互联网 发布:年轻人不愿生了知乎 编辑:程序博客网 时间:2024/06/07 20:32
1. 阻塞描述符(有文件描述符和Socket描述符)。调用 函数(read/write/recv/recvfrom/recvmsg)返回的时间。非阻塞是不管有没有数据,马上返回。阻塞是要等到有数据的时候再返回。Linux/Unix/Windows默认都是阻塞的。
(1)在Unix类系统设置
设置非阻塞:
int flags = fcntl(sockfd,F_GETFL) | O_NONBLOCK;
fcntl(sockfd,F_SETFL,flags);
设置阻塞:int flags = fcntl(sockfd,F_GETFL) & (~ O_NONBLOCK);
fcntl(sockfd,F_SETFL,flags);
(2)在Windows中设置
设置非阻塞
unsigned long isBlocked = 1;
ioctlsocket(s, FIONBIO, (unsigned long*)&isBlocked);
设置阻塞
unsigned long isBlocked = 0;
ioctlsocket(s, FIONBIO, (unsigned long*)&isBlocked);
2. Socket常用选项配置。
int setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen);
即在调用send/sendto之后,直接发送数据,可设置size = 0;
int size = 32 * 1024; // 32KB.
setsockopt(sockfd, SOL_SOCKET,SO_RCVBUF, &size, sizeof(size));
setsockopt(sockfd, SOL_SOCKET,SO_SNDBUF, &size, sizeof(size));
struct timeval tm = {0};
tm.tv_sec = N; // N seconds.
tm.tv_usec = M_us; //N us. 1000000us = 1s
setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm));
(1)、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
(2)、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。
(3)、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。
(4)、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。
只考虑AF_INET的情况(同一端口指ip地址与端口号都相同)
1.freebsd支持SO_REUSEPORT和SO_REUSEADDR选项,而linux只支持SO_REUSEADDR选项。
2.freebsd下,使用SO_REUSEPORT选项,两个tcp的socket可以绑定同一个端口;同样,使用SO_REUSEPORT选项,两个udp的socket可以绑定同一个端口。
3.linux下,两个tcp的socket不能绑定同一个端口;而如果使用SO_REUSEADDR选项,两个udp的socket可以绑定同一个端口。
4.freebsd下,两个tcp的socket绑定同一端口,只有第一个socket获得数据。
5.freebsd下,两个udp的socket绑定同一端口,如果数据包的目的地址是单播地址,则只有第一个socket获得数据,而如果数据包的目的地址是多播地址,则两个socket同时获得相同的数据。
6.linux下,两个udp的socket绑定同一端口,如果数据包的目的地址是单播地址,则只有最后一个socket获得数据,而如果数据包的目的地址是多播地址,则两个socket同时获得相同的数据。
有些系统如Linux,没有定义SO_REUSEPORT,可自行加上。
#define SO_REUSEPORT 15 )
int usable = 1;
setsockopt(sockfd, SOL_SOCKET ,SO_REUSEADDR, &usable , sizeof(usable ));
setsockopt(sockfd, SOL_SOCKET ,SO_REUSEPORT, &usable , sizeof(usable ));
int bBroadcast= 1;
setsockopt(sock,SOL_SOCKET,SO_BROADCAST, &bBroadcast, sizeof(BOOL));
u_short l_onoff; //linger开关
u_short l_linger; //等待时间,单位:秒};
struct linger llinger = {1, 5}; //1:打开linger选项,停留时间为5秒
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&llinger,sizeof(llinger));
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,&bDontLinger,sizeof(int));
int bConditionalAccept=1;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,&bConditionalAccept,sizeof(int ));
IPPROTO_TCP
IPPROTO_TCP
IPPROTO_TCP
TCP_KEEPCNT
TCP_KEEPIDLE
TCP_KEEPINTVL
int
int(单位:500ms)
int(单位:500ms)
TCP_KEEPCNT: 关闭一个非活跃连接之前进行探测的最大次数。默认为 8 次
TCP_KEEPINTVL:两个探测的时间间隔,默认值为 150 即 75 秒。
TCP_KEEPIDLE:对一个连接进行有效性探测之前运行的最大非活跃时间间隔,默认值为 14400(即 2 个小时) 。
int keepalive = 1; //开启keepalive
setsockopt(incomingsock,SOL_SOCKET, SO_KEEPALIVE,&keepalive, sizeof(keepalive));
int max_idle_time = 28800; //当socket处于IDLE时,4小时内要有真实数据通信.
setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE,&start_time ,sizeof(int));
int interval = 300; //间隔时间为150秒, 每150秒心跳一次。
setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, &interval , sizeof(interval));
int probes= 8; //最大探测次数为8
setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT, &probes, sizeof(probes));
当在传送大量数据的时候,为了提高TCP发送效率,可以设置TCP_CORK,CORK顾名思义,就是"塞子"的意思,它会尽量在每次发送最大的数据量。当设置了TCP_CORK后,会有阻塞200ms,当阻塞时间过后,数据就会自动传送。也是禁用了Nagle化。
#<netinet/tcp.h>
int enable = 1;
setsockopt(s,IPPROTO_TCP,TCP_NODELAY, &enable,sizeof(enable));
setsockopt(s,IPPROTO_TCP,TCP_CHORK, &enable,sizeof(enable));
由于设置TCP_DEFER_ACCEPT选项之后,三次握手后状态没有达到ESTABLISHED,而是SYN_RECV。这个时候,如果客户端一直没有发送"数据"报文,服务器将重传SYN/ACK报文,重传次数受net.ipv4.tcp_synack_retries参数控制,达到重传次数之后,才会再次进行setsockopt中设置的超时值,因此会出现SYN_RECV生存时间比设置值大一些的情况。
int max_seconds = 5;
setsockopt(s, TCP_DEFER_ACCEPT, &max_seconds , sizeof(max_seconds ));
char buffer[PCKT_LEN];
struct ipheader *iphdr = (struct ipheader *) buffer;
struct udpheader *udp = (struct udpheader *) (buffer + sizeof(struct ipheader));
int flag = 1;
setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &flag, sizeof(int));
recvfrom(sockfd, buffer, &len, 0, &addr, &addr_eln);
iphdr->.....;
sendto(sockfd, buffer, &len, 0, &addr, addr_eln);
3. 判断对方 TCP socket / UDP socket bound destination address是否关闭。
if(recv(sockfd, buf, 100) == 0) {
......
}
4. linux/epoll 函数.
ET / LT 工作模式:
ET模式仅当状态发生变化的时候才获得通知,这里所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就是说,如果要采用ET模式,需要一直read/write直到出错为止,很多人反映为什么采用ET模式只接收了一部分数据就再也得不到通知了,大多因为这样;而LT模式是只要有数据没有处理就会一直通知下去的.
#include <sys/epoll.h>
int epoll_create(int maxfdnum);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
int close(int epfd);
epfd: 是epoll_create返回的描述符。
op: EPOLL_CTL_ADD / EPOLL_CTL_MOD / EPOLL_CTL_DEL
fd: 要操作的描述符(这里就是socket 号)。
event: struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
maxevents: 必须小于等于maxfdnum。是监听的最大事件数。
events: 其实类型是struct epoll_event events[].
IN, 要监听的事件集合。
OUT, 发生事件的集合。 返回值是 集合的个数。 当返回 0 为超时,表示没有感兴趣的事件发生。
timeout: 超时时间,单位是毫秒。 -1为无限的不确定的等待。可以理解为阻塞的意思。
int count = epoll_wait(epfd, event, 12, -1);
int index = 0;
for(index = 0; index < count; index++) {
event[index].fd; //这个就是socket 套接字。
.........
}
5. 增加最大描述符数。我们都知道,通常一个终端下最多只能有1024个描述符,还有0,1,2默认被占用,加上设备/文件,有时候还真的不够用。怎么增加呢?
软限制/硬限制: 软限制是指内核所能支持的资源上限。硬限制只是作为软限制的上限。软限制不能超过硬限制。
命令设置: ulimit -n
ulimit -n 10000
关闭当前终端配置将自动失效。
编程设置:getrlimit,setrlimit/ RLIMIT_NOFILE
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlp);
int setrlimit(int resource, const struct rlimit *rlp);
int fun(int max) {
struct rlimit limit = {0};
getrlimit(RLIMIT_NOFILE, &limit );
limit.rlim_cur = limit.rlim_max;
limit.rlim_max = max;
setrlimit(RLIMIT_NOFILE, &limit );
return 0;
}
另外,还有RLIMIT_CPU / RLIMIT_STACK / RLIMIT_NPROC.
- Socket 编程 小知识库
- SOCKET编程小知识
- Socket编程小错误
- socket编程小范例
- Android小知识库(转)
- Android小知识库
- MFC小知识库
- VC SOCKET 编程小示例
- Linux Socket 编程小例
- java socket编程小例子
- socket编程UDP小例子
- SOCKET编程的小细节和误区
- 关于socket编程的一点小思考
- Socket编程 一个小的聊天程序
- 关于socket编程的小总结
- Linux socket编程之bind小技巧
- Java Socket编程实现聊天小案例
- linux下socket编程小总结
- Handler总结
- Android使用ant对项目进行编译签名优化打包
- spring bean 的配置-不同构造函数
- SonicUI在MFC中的使用
- WebService Myeclipse Web Tomcat SOAP
- Socket 编程 小知识库
- 代码生成工具,代码生成器,三层架构生成神具,三层也是浮云
- mailto 重定向
- jquery
- 关于ioremap,request_mem_region(转)
- Android 多Activity下的 menu 处理
- CollectionHelper-网页采集辅助类
- python 正则表达式
- Xlib: connection to ":0.0" refused by server解决方法