最近各种忙,博客也闲置了近两个多月。这次的干货是关于Socket相关的一些有用的资料。收集一下,就当整理一下收藏夹了。免得每次都要Google重新找一遍。
首先先推荐一本书。这本书躺在图书馆了基本没什么人用,我就Renew了好几次,借了几个月。虽然内容是比较老的C样式的程序,但是比我看过的很多教材都要好。感觉是通俗易懂的同时,涵盖了很多细节上的东西。说起来,Socket这一套东西,这么多年都没什么变化,究竟有没有人在继续维护这套东西的?我比较担心。
UNIX network programming
W. Richard Stevens, Bill Fenner, Andrew M. Rudoff.
Boston, MA : Addison-WesleyCall Number: QA 76.76 O63 S755 2004 V.1
首先是Open Group 的网页,一般可以将这个页面作为起点寻找Socket相关的函数。accept() bind() connect() 之类的就不再赘述了,在这个网站上面查就是了。
http://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/socket.h.html
堵塞和非堵塞recv(), send()方面,主要用的是fcntl() 的 O_NONBLOCK
.
fcntl(socket_des, F_SETFL, fcntl(socket_des, F_GETFL) | O_NONBLOCK);
Stack Overflow 上面有个回答总结得很好,关于返回recv状态方面的:https://stackoverflow.com/a/17831323/2924381
还有关于send状态方面的:https://stackoverflow.com/a/19400029/2924381
存地址方面,经常要用到这几个结构:sockaddr, sockaddr_in, sockaddr_in6, sockaddr_un, sockaddr_storage。一般情况下,我们可以分配一个 sockaddr_storage,这样就有足够的内存用来存放socket的地址了,对于指针方面,则可以使用sockaddr。真正使用的时候再检查sa_family
. 然后再将变量cast 成别的socket的类型。
sockaddr * address = (sockaddr *) new sockaddr_storage();
struct sockaddr { sa_family_t sa_family; //Address family. char sa_data[]; //Socket address (variable-length data). } struct sockaddr_storage { sa_family_t ss_family; /* Address family. */ /* * 这里将填充有足够的位置。。。。。 */ } struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; struct in_addr { uint32_t s_addr; /* address in network byte order */ }; struct sockaddr_in6 { sa_family_t sin6_family; /* AF_INET6 */ in_port_t sin6_port; /* port number */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */ }; struct in6_addr { unsigned char s6_addr[16]; /* IPv6 address */ };
等待某事件的发生,类似轮询,很多人用selet() 和 poll() 这两个函数。但前者效率低,后者很多坑,建议使用libevent或者libev。libev方面的文档在这里。
最后注意要处理一下Socket的SIGPIPE信号
//需要引用 #include <csignal> signal(SIGPIPE, SIG_IGN);