??xml version="1.0" encoding="utf-8" standalone="yes"?>久久久久久久综合日本亚洲,久久91精品国产91久久麻豆,久久久久久久亚洲Av无码 http://www.shnenglu.com/API/category/16122.htmlzh-cn Tue, 12 Dec 2017 03:06:12 GMT Tue, 12 Dec 2017 03:06:12 GMT 60 套接字read/writeq回?/title> http://www.shnenglu.com/API/archive/2017/12/12/215420.htmlC++技术中?/dc:creator>C++技术中?/author>Tue, 12 Dec 2017 02:32:00 GMT http://www.shnenglu.com/API/archive/2017/12/12/215420.html http://www.shnenglu.com/API/comments/215420.html http://www.shnenglu.com/API/archive/2017/12/12/215420.html#Feedback 0 http://www.shnenglu.com/API/comments/commentRss/215420.html http://www.shnenglu.com/API/services/trackbacks/215420.html 1、阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有区别?Q就我目前了解阻塞与非阻塞recvq回值没有区分,都是 <0Q出错,=0Q连接关闭,>0接收到数据大,特别Q返回?nbsp;<0时ƈ?errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认ؓq接是正常的Ql接收。只是阻塞模式下recv会阻塞着接收数据Q非d模式下如果没有数据会q回Q不会阻塞着读,因此需?nbsp;循环d
2、阻塞模式与非阻塞模式下write的返回值各代表什么意思?有没有区别?
d与非dwriteq回值没有区分,都是 <0Q出错,=0Q连接关闭,>0发送数据大,特别Q返回?nbsp;<0时ƈ?errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认ؓq接是正常的Ql发送。只是阻塞模式下write会阻塞着发送数据,非阻塞模式下如果暂时无法发送数据会q回Q不会阻塞着 writeQ因此需要@环发?/p>
3、阻塞模式下readq回?nbsp;< 0 && errno != EINTR && errno != EWOULDBLOCK && errno != EAGAINӞq接异常Q需要关闭,readq回?nbsp;< 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)时表C没有数据,需要l接Ӟ如果q回值大?表示接送到数据?nbsp;
非阻塞模式下readq回?nbsp;< 0表示没有数据Q? 0表示q接断开Q?gt; 0表示接收到数据?nbsp;
q?U模式下的返回值是不是q么理解Q有没有跟详l的理解或跟准确的说明?
4、阻塞模式与非阻塞模式下是否sendq回?nbsp;< 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)表示暂时发送失败,需要重试,如果sendq回?nbsp;<= 0, && errno != EINTR && errno != EWOULDBLOCK && errno != EAGAINӞq接异常Q需要关闭,如果sendq回?nbsp;> 0则表C发送了数据Qsend的返回值是否这么理解,d模式与非d模式下sendq回?0是否都是发送失败,q是那个模式下表C暂时不可发送,需?nbsp;重发Q?/p>
1. send函数
int send( SOCKET s, const char FAR *buf, int len, int flags );
不论是客Lq是服务器端应用E序都用send函数来向TCPq接的另一端发送数据?/p>
客户端程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户E序发送应{?/p>
该函数的Q?/p>
W一个参数指定发送端套接字描q符Q?/p>
W二个参数指明一个存攑ֺ用程序要发送数据的~冲区;
W三个参数指明实际要发送的数据的字节数Q?/p>
W四个参C般置0?/p>
q里只描q同步Socket的send函数的执行流E。当调用该函数时Qsend先比较待发送数据的长度len和套接字s的发送缓冲的长度Q如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERRORQ如果len于或者等于s的发送缓冲区的长度,那么send先检查协?nbsp;是否正在发送s的发送缓冲中的数据,如果是就{待协议把数据发送完Q如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据Q那?nbsp;send比较s的发送缓冲区的剩余空间和lenQ如果len大于剩余I间大小send׃直等待协议把s的发送缓冲中的数据发送完Q如果len于剩余 I间大小send׃仅把buf中的数据copy到剩余空间里Q?span style="box-sizing: border-box; margin: 0px; padding: 0px; color: #000000;">注意q不是send把s的发送缓冲中的数据传到连接的另一端的Q而是协议传的Qsend仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里Q?/span>如果send函数copy数据成功Q就q回实际copy的字节数Q如果send在copy数据时出现错误,那么sendp回SOCKET_ERRORQ如果send在等待协议传送数据时|络断开的话Q那么send函数也返回SOCKET_ERROR?/p>要注意send 函数把buf中的数据成功copy到s的发送缓冲的剩余I间里后它就q回了,但是此时q些数据q不一定马上被传到q接的另一?span style="box-sizing: border-box; margin: 0px; padding: 0px; color: #000000;">?/span>如果协议在后l的传送过E中出现|络错误的话Q那么下一个Socket函数׃q回SOCKET_ERROR。(每一个除send外的Socket函数在执 行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能l,如果在等待时出现|络错误Q那么该Socket函数p?nbsp;SOCKET_ERRORQ?/p>
注意Q在Unixpȝ下,如果send在等待协议传送数据时|络断开的话Q调用send的进E会接收C个SIGPIPE信号Q进E对该信L默认处理是进E终止?/p>
Send函数的返回值有三类Q?/p>
Q?Q返回?0Q?/p>
Q?Q返回?lt;0Q发送失败,错误原因存于全局变量errno?/p>
Q?Q返回?gt;0Q表C发送的字节敎ͼ实际上是拯到发送缓冲中的字节数Q?/p>
错误代码Q?/p>
EBADF 参数s 非合法的socket处理代码?br style="box-sizing: border-box;" />EFAULT 参数中有一指针指向无法存取的内存空?br style="box-sizing: border-box;" />ENOTSOCK 参数sZ文g描述词,非socket?br style="box-sizing: border-box;" />EINTR 被信h中断?br style="box-sizing: border-box;" />EAGAIN 此操作会令进E阻断,但参数s的socketZ可阻断?br style="box-sizing: border-box;" />ENOBUFS pȝ的缓冲内存不?br style="box-sizing: border-box;" />ENOMEM 核心内存不 EINVAL 传给pȝ调用的参C正确?/p>
2. recv函数
int recv( SOCKET s, char FAR *buf, int len, int flags );
不论是客Lq是服务器端应用E序都用recv函数从TCPq接的另一端接收数据?/p>
该函数的Q?/p>
W一个参数指定接收端套接字描q符Q?/p>
W二个参数指明一个缓冲区Q该~冲区用来存放recv函数接收到的数据Q?/p>
W三个参数指明buf的长度;
W四个参C般置0?/p>
q里只描q同步Socket的recv函数的执行流E。当应用E序调用recv函数Ӟrecv先等待s的发送缓?nbsp;中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现|络错误Q那么recv函数q回SOCKET_ERRORQ如果s的发送缓冲中没有?nbsp;据或者数据被协议成功发送完毕后Qrecv先检查套接字s的接收缓冲区Q如果s接收~冲Z没有数据或者协议正在接收数据,那么recv׃直等待,只到 协议把数据接收完毕。当协议把数据接收完毕,recv函数把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf 的长度,所?nbsp;在这U情况下要调用几ơrecv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据Q真正的接收数据是协议来完成的)Qrecv函数q回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERRORQ如果recv函数在等待协议接收数据时|络中断了,那么它返??/p>
注意Q在Unixpȝ下,如果recv函数在等待协议接收数据时|络断开了,那么调用recv的进E会接收C个SIGPIPE信号Q进E对该信L默认处理是进E终止?/p>
默认情况下socket是阻塞的?/p>
d与非drecvq回值没有区别,都是Q?/p>
<0 出错
=0 Ҏ调用了close API来关闭连?/p>
>0 接收到的数据大小Q?/p>
特别圎ͼq回?lt;0时ƈ?errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认ؓq接是正常的Ql接收?/p>
只是d模式下recv会一直阻塞直到接收到数据Q非d模式下如果没有数据就会返回,不会d着读,因此需要@环读取)?/p>
q回说明Q?nbsp;
Q?Q成功执行时Q返回接收到的字节数?/p>
Q?Q若另一端已关闭q接则返?Q这U关闭是Ҏd且正常的关闭
Q?Q失败返?1Qerrno被设Z下的某个?nbsp;
EAGAINQ套接字已标Cؓ非阻塞,而接收操作被d或者接收超?/p>
EBADFQsock不是有效的描q词
ECONNREFUSEQ远E主机阻l网l连?/p>
EFAULTQ内存空间访问出?/p>
EINTRQ操作被信号中断
EINVALQ参数无?/p>
ENOMEMQ内存不?/p>
ENOTCONNQ与面向q接兌的套接字未被连接上
ENOTSOCKQsock索引的不是套接字
]]>Echo Server based on libevent http://www.shnenglu.com/API/archive/2015/05/03/210531.htmlC++技术中?/dc:creator>C++技术中?/author>Sun, 03 May 2015 12:28:00 GMT http://www.shnenglu.com/API/archive/2015/05/03/210531.html http://www.shnenglu.com/API/comments/210531.html http://www.shnenglu.com/API/archive/2015/05/03/210531.html#Feedback 0 http://www.shnenglu.com/API/comments/commentRss/210531.html http://www.shnenglu.com/API/services/trackbacks/210531.html 出处QBlog of Felix021
旉QSat, 25 Feb 2012 00:43:26 +0000
作者:felix021
地址Qhttp://www.felix021.com/blog/read.php?2068
内容Q?/div>
׃两天的时间在libevent上,xȝ下,׃写简单tutorial的方式吧Q貌似没有一简单的说明Q让人马上就能上手用的?/div>
首先l出官方文档吧: http://libevent.org Q首|个Programming with LibeventQ里面是一节一节的介绍libeventQ但是感觉信息量太大了,而且q是英文??Q当Ӟ如果惛_好用libeventQ看看还是很有必要的Q,q有个ReferenceQ大致就是对各个版本的libevent使用doxgen生成的文档,用来查函数原型和基本用法什么的?/div>
下面假定已经学习q基本的socket~程Qsocket,bind,listen,accept,connect,recv,send,closeQ,q且对异?callback有基本认识?/div>
基本的socket~程是阻?同步的,每个操作除非已经完成或者出错才会返回,q样对于每一个请求,要用一个线E或者单独的q程d理,pȝ资源没法支撑大量的请求(所谓c10k problem?Q,例如内存Q默认情况下每个U程需要占??M的栈I间。posix定义了可以用异步的selectpȝ调用Q但是因为其采用了轮询的方式来判断某个fd是否变成activeQ效率不高[O(n)]Q连接数一多,也还是撑不住。于是各pȝ分别提出了基于异?callback的系l调用,例如Linux的epollQBSD的kqueueQWindows的IOCP。由于在内核层面做了支持Q所以可以用O(1)的效率查扑ֈactive的fd。基本上Qlibevent是对这些高效IO的封装,提供l一的APIQ简化开发?/div>
libevent大概是这LQ?/div>
默认情况下是单线E的Q可以配|成多线E,如果有需要的话)Q每个线E有且只有一个event_baseQ对应一个struct event_basel构体(以及附于其上的事件管理器Q,用来schedule托管l它的一pdeventQ可以和操作pȝ的进E管理类比,当然Q要更简单一炏V当一个事件发生后Qevent_base会在合适的旉Q不一定是立即Q去调用l定在这个事件上的函敎ͼ传入一些预定义的参敎ͼ以及在绑定时指定的一个参敎ͼQ直到这个函数执行完Q再q回schedule其他事g?/创徏一个event_base
struct event_base *base = event_base_new();
assert(base != NULL);
event_base内部有一个@环,循环d在epoll/kqueue{系l调用上Q直到有一?一些事件发生,然后d理这些事件。当Ӟq些事g要被l定在这个event_base上。每个事件对应一个struct eventQ可以是监听一个fd或者POSIX信号量之c(q里只讲fd了,其他的看manual吧)。struct event使用event_new来创建和l定Q用event_add来启用://创徏q绑定一个event
struct event *listen_event;
//参数Qevent_base, 监听的fdQ事件类型及属性,l定的回调函敎ͼl回调函数的参数
listen_event = event_new(base, listener, EV_READ|EV_PERSIST, callback_func, (void*)base);
//参数QeventQ超时时?struct timeval *cd的,NULL表示无超时设|?
event_add(listen_event, NULL);
注:libevent支持的事件及属性包?使用bitfield实现Q所以要?| 来让它们合体)
(a) EV_TIMEOUT: 时
(b) EV_READ: 只要|络~冲中还有数据,回调函数׃被触?/div>
(c) EV_WRITE: 只要塞给|络~冲的数据被写完Q回调函数就会被触发
(d) EV_SIGNAL: POSIX信号量,参考manual?/div>
(e) EV_PERSIST: 不指定这个属性的话,回调函数被触发后事g会被删除
(f) EV_ET: Edge-Trigger边缘触发Q参考EPOLL_ET
然后需要启动event_base的@环,q样才能开始处理发生的事g。@环的启动使用event_base_dispatchQ@环将一直持l,直到不再有需要关注的事gQ或者是遇到event_loopbreak()/event_loopexit()函数?/启动事g循环
event_base_dispatch(base);
接下来关注下l定到event的回调函数callback_funcQ传递给它的是一个socket fd、一个eventcd及属性bit_field、以及传递给event_new的最后一个参敎ͼM面几行回一下,把event_basel传q来了,实际上更多地是分配一个结构体Q把相关的数据都撂进去,然后丢给event_newQ在q里p取得CQ。其原型是:typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)
对于一个服务器而言Q上面的程大概是这L合的Q?/div>
1. listener = socket()Qbind()Qlisten()Q设|nonblocking(POSIXpȝ中可使用fcntl讄Qwindows不需要设|,实际上libevent提供了统一的包装evutil_make_socket_nonblocking)
2. 创徏一个event_base
3. 创徏一个eventQ将该socket托管levent_baseQ指定要监听的事件类型,q绑定上相应的回调函?及需要给它的参数)。对于listener socket来说Q只需要监听EV_READ|EV_PERSIST
4. 启用该事?/div>
5. q入事g循环
---------------
6. (异步) 当有client发vh的时候,调用该回调函敎ͼq行处理?/div>
问题Qؓ什么不在listen完马上调用acceptQ获得客Lq接以后再丢levent_base呢?q个问题先想惛_?/div>
回调函数要做什么事情呢Q当然是处理client的请求了。首先要acceptQ获得一个可以与client通信的sockfdQ然?#8230;…调用recv/send吗?错!大错牚wQ如果直接调用recv/send的话Q这个线E就d在这个地方了Q如果这个客L非常的阴险(比如一直不发消息,或者网l不好,老是丢包Q,libevent只能等它,没法处理其他的请求了——所以应该创Z个新的event来托这个sockfd?/div>
在老版本libevent上的实现Q比较罗嗦[如果不想详细了解的话Q看下一部分]?/div>
对于服务器希望先从client获取数据的情况,大致程是这LQ?/div>
1. 这个sockfd讄为nonblocking
2. 创徏2个event:
event_readQ绑上sockfd的EV_READ|EV_PERSISTQ设|回调函数和参数Q后面提到的structQ?/div>
event_writeQ绑上sockfd的EV_WRITE|EV_PERSISTQ设|回调函数和参数Q后面提到的structQ?/div>
3. 启用event_read事g
------
4. (异步) {待event_read事g的发? 调用相应的回调函数。这里麻烦来了:回调函数用recvd的数据,不能直接用send丢给sockfd了事——因ؓsockfd是nonblocking的,丢给它的话,不能保证正确Qؓ什么呢Q)。所以需要一个自q理的~存用来保存d的数据中Q在accept以后创Z个structQ作为第2步回调函数的arg传进来)Q在合适的旉Q比如遇到换行符Q启用event_write事g【event_add(event_write, NULL)】,{待EV_WRITE事g的触?/div>
------
5. (异步) 当event_write事g的回调函数被调用的时候,往sockfd写入数据Q然后删除event_write事g【event_del(event_write)】,{待event_read事g的下一ơ执行?/div>
以上步骤比较晦ӆQ具体代码可参考官Ҏ档里面的【Example: A low-level ROT13 server with Libevent?/div>
׃需要自q理缓冲区Q且q程晦ӆ难懂Qƈ且不兼容于Windows的IOCPQ所以libevent2开始,提供了buffereventq个器Q用来提供更加优雅、易用的API。struct bufferevent内徏了两个event(read/write)和对应的~冲区【struct evbuffer *input, *output】,q提供相应的函数用来操作~冲区(或者直接操作buffereventQ。每当有数据被读入input的时候,read_cb函数被调用;每当output被输出完的时候,write_cb被调用;在网lIO操作出现错误的情况(q接中断、超时、其他错误)Qerror_cb被调用。于是上一部分的步骤被化ؓQ?/div>
1. 讄sockfd为nonblocking
2. 使用bufferevent_socket_new创徏一个struct bufferevent *bevQ关联该sockfdQ托给event_base
3. 使用bufferevent_setcb(bev, read_cb, write_cb, error_cb, (void *)arg)EV_READ/EV_WRITE对应的函?/div>
4. 使用bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST)来启用read/write事g
------
5. (异步)
在read_cb里面从inputd数据Q处理完毕后塞到output?会被自动写入到sockfd)
在write_cb里面Q需要做什么吗Q对于一个echo server来说Qread_cbp够了Q?/div>
在error_cb里面处理遇到的错?/div>
*. 可以使用bufferevent_set_timeouts(bev, struct timeval *READ, struct timeval *WRITE)来设|读写超? 在error_cb里面处理时?/div>
*. read_cb和write_cb的原型是
void read_or_write_callback(struct bufferevent *bev, void *arg)
error_cb的原型是
void error_cb(struct bufferevent *bev, short error, void *arg) //q个是event的标准回调函数原?/div>
可以从bev中用libevent的API提取出event_base、sockfd、input/output{相x据,详情RTFM~
于是代码化到只需要几行的read_cb和error_cb函数卛_Q?/div>
void read_cb(struct bufferevent *bev, void *arg) {
char line[256];
int n;
evutil_socket_t fd = bufferevent_getfd(bev);
while (n = bufferevent_read(bev, line, 256), n > 0)
bufferevent_write(bev, line, n);
}
void error_cb(struct bufferevent *bev, short event, void *arg) {
bufferevent_free(bev);
}
于是一个支持大q发量的echo server成型了Q下面附上无注释的echo server源码Q?10行,多抄几遍Q就能完全弄懂啦Q更复杂的例子参见官Ҏ档里面的【Example: A simpler ROT13 server with Libevent?/div>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#define LISTEN_PORT 9999
#define LISTEN_BACKLOG 32
void do_accept(evutil_socket_t listener, short event, void *arg);
void read_cb(struct bufferevent *bev, void *arg);
void error_cb(struct bufferevent *bev, short event, void *arg);
void write_cb(struct bufferevent *bev, void *arg);
int main(int argc, char *argv[])
{
int ret;
evutil_socket_t listener;
listener = socket(AF_INET, SOCK_STREAM, 0);
assert(listener > 0);
evutil_make_listen_socket_reuseable(listener);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(LISTEN_PORT);
if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("bind");
return 1;
}
if (listen(listener, LISTEN_BACKLOG) < 0) {
perror("listen");
return 1;
}
printf ("Listening...\n");
evutil_make_socket_nonblocking(listener);
struct event_base *base = event_base_new();
assert(base != NULL);
struct event *listen_event;
listen_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
event_add(listen_event, NULL);
event_base_dispatch(base);
printf("The End.");
return 0;
}
void do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = (struct event_base *)arg;
evutil_socket_t fd;
struct sockaddr_in sin;
socklen_t slen = sizeof(sin);
fd = accept(listener, (struct sockaddr *)&sin, &slen);
if (fd < 0) {
perror("accept");
return;
}
if (fd > FD_SETSIZE) { //q个if是参考了那个ROT13的例子,貌似是官方的疏漏Q从select-based例子里抄q来忘了?/div>
perror("fd > FD_SETSIZE\n");
return;
}
printf("ACCEPT: fd = %u\n", fd);
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST);
}
void read_cb(struct bufferevent *bev, void *arg)
{
#define MAX_LINE 256
char line[MAX_LINE+1];
int n;
evutil_socket_t fd = bufferevent_getfd(bev);
while (n = bufferevent_read(bev, line, MAX_LINE), n > 0) {
line[n] = '\0';
printf("fd=%u, read line: %s\n", fd, line);
bufferevent_write(bev, line, n);
}
}
void write_cb(struct bufferevent *bev, void *arg) {}
void error_cb(struct bufferevent *bev, short event, void *arg)
{
evutil_socket_t fd = bufferevent_getfd(bev);
printf("fd = %u, ", fd);
if (event & BEV_EVENT_TIMEOUT) {
printf("Timed out\n"); //if bufferevent_set_timeouts() called
}
else if (event & BEV_EVENT_EOF) {
printf("connection closed\n");
}
else if (event & BEV_EVENT_ERROR) {
printf("some other error\n");
}
bufferevent_free(bev);
}
]]>
libevent windows~译 http://www.shnenglu.com/API/archive/2015/02/15/209824.htmlC++技术中?/dc:creator>C++技术中?/author>Sun, 15 Feb 2015 05:14:00 GMT http://www.shnenglu.com/API/archive/2015/02/15/209824.html http://www.shnenglu.com/API/comments/209824.html http://www.shnenglu.com/API/archive/2015/02/15/209824.html#Feedback 3 http://www.shnenglu.com/API/comments/commentRss/209824.html http://www.shnenglu.com/API/services/trackbacks/209824.html 1. 下蝲libevent?br />2.本h用vs2013~译Q所有需要修?br />
在以?/span>3个文件开头修?#8220; #define _WIN32_WINNT 0x0603”
libevent-2.0.22-stable\event_iocp.c
libevent-2.0.22-stable\evthread_win32.c
libevent-2.0.22-stable\listener.c 3.讄nmake的环?br />VC6 prefix\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT VC8 prefix\Microsoft Visual Studio 8\VC\bin\vcvars32.bat VC9 prefix\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat 我这里是vs2013Q执行D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat 注意Q这些批处理文g只会在当前进E中讄Q局部的Q环境变量,也就是说Q?br />1. 用cmdQ或者commandQ打开的命令行H口中, q行某个vcvar32.bat一ơ?br />那么当前命o行窗口中可以正怋用clQ直到关闭?br /> 4.使用VC的nmake -f Makefile.nmake卛_~译32位release模式?/span>如果要求~译64位的版本Q需要在Makefile.nmake中添加一个LIBFLAGS选项 /MACHINE:X64 如果要加调试信息Q可以在 CFLAGS中加?ZiQ?2位加调试选项?CFLAGS中加/ZIQ当然要调整优化选项/Ox
]]>
tcpq接探测Keepalive和心跛_ http://www.shnenglu.com/API/archive/2013/08/13/202516.htmlC++技术中?/dc:creator>C++技术中?/author>Tue, 13 Aug 2013 01:03:00 GMT http://www.shnenglu.com/API/archive/2013/08/13/202516.html http://www.shnenglu.com/API/comments/202516.html http://www.shnenglu.com/API/archive/2013/08/13/202516.html#Feedback 1 http://www.shnenglu.com/API/comments/commentRss/202516.html http://www.shnenglu.com/API/services/trackbacks/202516.html 采用TCPq接的C/S模式软gQ连接的双方在连接空闲状态时Q如果Q意一Ҏ外崩溃、当机、网U断开或\由器故障Q另一Ҏ法得知TCPq接已经失效Q除非l在此连接上发送数据导致错误返回。很多时候,q不是我们需要的。我们希望服务器端和客户端都能及时有效地到q接失效Q然后优雅地完成一些清理工作ƈ把错误报告给用户?br /> 如何及时有效地检到一方的非正常断开Q一直有两种技术可以运用。一U是由TCP协议层实现的KeepaliveQ另一U是由应用层自己实现的心跛_ ?br /> TCP默认q不开启Keepalive功能Q因为开启Keepalive功能需要消耗额外的宽带和流量,管q微不道,但在按流量计费的环境下增加了费用Q另一斚wQKeepalive讄不合理时可能会因为短暂的|络波动而断开健康的TCPq接。ƈ且,默认的Keepalive时需?,200,000 millisecondsQ即2时Q探次Cؓ5ơ?/span> 对于Win2K/XP/2003Q可以从下面的注册表Ҏ到媄响整个系l所有连接的keepalive参数Q?/span> [HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters] "KeepAliveTime”=dword:006ddd00 "KeepAliveInterval"=dword:000003e8 "MaxDataRetries"="5" 对于实用的程序来_2时的空闲时间太ѝ因此,我们需要手工开启Keepalive功能q设|合理的Keepalive参数?
// 开启KeepAlive BOOL bKeepAlive = TRUE; int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, ( char * ) & bKeepAlive, sizeof (bKeepAlive)); if (nRet == SOCKET_ERROR) { return FALSE; } // 讄KeepAlive参数 tcp_keepalive alive_in = { 0 } ; tcp_keepalive alive_out = { 0 } ; alive_in.keepalivetime = 5000 ; // 开始首ơKeepAlive探测前的TCPI闭旉 alive_in.keepaliveinterval = 1000 ; // 两次KeepAlive探测间的旉间隔 alive_in.onoff = TRUE; unsigned long ulBytesReturn = 0 ; nRet = WSAIoctl(socket_handle, SIO_KEEPALIVE_VALS, & alive_in, sizeof (alive_in), & alive_out, sizeof (alive_out), & ulBytesReturn, NULL, NULL); if (nRet == SOCKET_ERROR) { return FALSE; }
开启Keepalive选项之后Q对于用IOCP模型的服务器端程序来_一旦检到q接断开QGetQueuedCompletionStatus函数立卌回FALSEQ得服务器端能及时清除该连接、释放该q接相关的资源。对于用select模型的客L来说Q连接断开被探到Ӟ以recv目的d在socket上的selectҎ立卌回SOCKET_ERRORQ从而得知连接已失效Q客LE序便有Z及时执行清除工作、提醒用h重新q接?br />
另一U技术,由应用程序自己发送心跛_来检连接的健康性。客L可以在一个Timer中或低别的U程中定时向发服务器发送一个短精悍的包,q等待服务器的回应。客LE序在一定时间内没有收到服务器回应即认ؓq接不可用,同样Q服务器在一定时间内没有收到客户端的心蟩包则认ؓ客户端已l掉Uѝ?br />
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
windows下此处的”非正常断开”指TCPq接不是以优雅的方式断开,如网U故障等物理链\的原?q有H然L断电{原?
有两U方法可以检?
1.TCPq接双方定时发握手消?
2.利用TCP协议栈中的KeepAlive探测 W二U方法简单可?只需对TCPq接两个Socket讑֮KeepAlive探测, 所以本文只讲第二种Ҏ在Linux,Window2000下的实现(在其它的q_上没有作q一步的试)
Windows 2000q_?nbsp;头文?br /> #include < mstcpip.h > // 定义l构及宏 /* struct TCP_KEEPALIVE { u_longonoff; u_longkeepalivetime; u_longkeepaliveinterval; } ; */ tcp_keepalive live,liveout; live.keepaliveinterval=5000; //?U发一ơ探报??ơ没有回应,断开 live.keepalivetime=30000;//过30s没有数据Q就发送控包 live.onoff=TRUE; int Opt = 1; int iRet = setsockopt(Accept,SOL_SOCKET,SO_KEEPALIVE,(char *)&Opt,sizeof(int)); if(iRet == 0){ DWORD dw; if(::WSAIoctl(Accept,SIO_KEEPALIVE_VALS, &live,sizeof(live),&liveout,sizeof(liveout), &dw,NULL,NULL)== SOCKET_ERROR){ } }
ACE下代?//by rainfish blog.csdn.net/bat603
int Opt = 1; //在测试过E中Q发现检的ơ数?ơ,即下面的讄中,从最q一ơ消息开始计的10U后Q每ơ间?U,q箋发?ơ,?5U发现网l断?br />tcp_keepalive live,liveout; live.keepaliveinterval=5000; //每次的间隔 Q单位毫U) live.keepalivetime=10000; //W一ơ开始发送的旉Q单位毫U) live.onoff=TRUE; int iRet = stream.set_option(SOL_SOCKET,SO_KEEPALIVE,&Opt,sizeof(int)); if(iRet == 0){ DWORD dw; //此处昄了在ACE下获取套接字的方法,卛_柄的(SOCKET)化就是句?br />if(WSAIoctl((SOCKET)h,SIO_KEEPALIVE_VALS,&live,sizeof(live), &liveout,sizeof(liveout),&dw,NULL,NULL)== SOCKET_ERROR){ //Delete Client return; } }
Linuxq_?/p>
#include "/usr/include/linux/tcp.h" #include "/usr/include/linux/socket.h" ////KeepAlive实现Q单位秒 //下面代码要求有ACE,如果没有包含ACE,则请把用到的ACE函数Ҏlinux相应的接?br />int keepAlive = 1;//讑֮KeepAlive int keepIdle = 5;//开始首ơKeepAlive探测前的TCPI闭旉 int keepInterval = 5;//两次KeepAlive探测间的旉间隔 int keepCount = 3;//判定断开前的KeepAlive探测ơ数 if(setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) setsockopt SO_KEEPALIVE error!/n"))); }
if(setsockopt(s,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == -1) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPIDLE error!/n"))); }
if(setsockopt(s,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == -1) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPINTVL error!/n"))); }
if(setsockopt(s,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == -1) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t)setsockopt TCP_KEEPCNT error!/n"))); }
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
]]> UDP,TCP打洞技?/title> http://www.shnenglu.com/API/archive/2012/08/24/188130.htmlC++技术中?/dc:creator>C++技术中?/author>Fri, 24 Aug 2012 06:18:00 GMT http://www.shnenglu.com/API/archive/2012/08/24/188130.html http://www.shnenglu.com/API/comments/188130.html http://www.shnenglu.com/API/archive/2012/08/24/188130.html#Feedback 3 http://www.shnenglu.com/API/comments/commentRss/188130.html http://www.shnenglu.com/API/services/trackbacks/188130.html 1、介l?/p>
用户量高速增长以及大量安全问题的巨大压力qInternet技术不断向前发展,但是q些新兴的技术很大程度地增加了应用程序开发的成本和复杂性。Internet最初的地址体系是每个节Ҏ一个唯一不变的全局地址Q可以通过该地址直接与Q何其它的节点q行通信Q而现如今Q该地址体系已经被新的实际上q泛使用的地址体系所替换Q新的地址体系是由全局地址域和通过NAT接入全局地址域的大量U有地址域组成。在新的地址体系中(如图1所C)Q只有在 “main”全局地址域中的节点可以在|络中很ҎCM其它的拥有全局地址的节炚w信Q因节点拥有全局的、唯一的、可路由的地址。在U有|络中的节点可以与在同一个私有网l中的其它节点进行通信Qƈ且在通常情况下可以向全局地址中的某个“著名”的节点发起TCPq接或发送UDP数据包。NAT讑֤在此扮演的角色就是ؓ从内|向公网发v的连接的节点分配临时的{发sessionQ将来自内网的数据包的地址和端口{换ؓ公网的地址和端口,来自公|的数据包的地址和端口{换ؓ内网的端口和地址Q同时NAT屏蔽所有未l授权的来自公网的数据包?/p>
新的Internet地址体系非常适合?#8220;客户?服务?#8221;q样的通信模式Q一个典型的C/S通信模式是:客户端在内网Q私有地址域)Q服务器在公|(全局地址域)Q通过NAT内|和公网q接h。这U地址体系使得在不同内|(U有地址域)中的两个节点很难直接通信Q而这恰恰是p2p应用 (如,电话会议或在U游?中最基本的要求。很昄Q我们需要一U方法即使在NAT讑֤存在的前提下Q仍然能够无障碍地实现p2p通信?/div>
在不同内|的两个节点之间建立p2pq接的最有效的方法就?#8220;打洞”。该技术在ZUDP的应用程序中得到了广泛的应用Q同LQ该技术也可以用于ZTCP的应用程序。有的是,?#8220;打洞”字面上的意思刚好相反,该技术不会媄响到内网的安全。事实上Q?#8220;打洞”技术得p2p软g的绝大部分功能都在NAT讑֤默认的安全策略的控制之下Q这些都由NAT讑֤建立的session来管理。本文阐qC适用于UDP和TCP?#8220;打洞”技术,q详l描qC重要“打洞”q程中,应用E序和NAT讑֤之间的行为?/div>
不幸的是Q由于NAT讑֤的响应和行ؓ不是标准的,所以没有Q何技术可以穿现有的所有NAT讑֤。本文提供了一些在现有NAT讑֤上进?#8220;打洞”的实验结果。我们收集的数据来自于互联网上用了“NAT Check”工具q在大量不同生厂商的NAT讑֤上进?#8220;打洞”实验的用戗由于数据是来自于一个叫?#8220;self-selecting”的用L区,或许不会完全代表在Internet上真正部|和使用的NAT讑֤Q但是结果无论如何还是很令h兴奋的?/div>
在做基本?#8220;打洞”操作评估的时候,我们应该指出在现有的NAT讑֤“打洞”的复杂度上,不同的复杂度会有不同的结果。但目前我们把讨论的重点集中于开发最单的Q可以应用于M|络拓扑l构的、稳定的、有正确NAT响应的NAT讑֤上的“打洞”技术。我们有意避免用一?#8220;聪明的小把戏”通过ƺ骗某些NAT讑֤来达到短期内I越较多的NAT讑֤Q但从长期来看会引v|络未知错误的技术?/div>
管引入IPv6会极大地增加互联|的地址I间Q从而减对于NAT讑֤的需求量Q但短期内IPv6实增加了对NAT讑֤的需求量Q因?NAT讑֤本n提供了一U方便的Ҏq行IPv4与IPv6地址域{换。另外私有网l上建立匿名和加密访问节点也有利于组l机构的安全性以及不受外界干扎ͼq些都意味着NATq将存在相当长的一D|间。同P防火墙技术也不会׃有了_的ip地址而消失,IPv6的防火墙仍然会默认丢掉所有未l授权的数据包,仍然可以让在IPv6环境下工作的应用E序“打洞”?/div>
本文接下来的部分按照如下的方式组l:W二章介l基本的NATI越概念和术语;W三章介lUDP“打洞”q程Q第四章介绍TCP“打洞”q程Q第五章介绍支持“打洞”的NAT讑֤必须h那些Ҏ;W六章介l我们在目前行的NAT讑֤上的“打洞”实验l果Q第七章讨论相关的网l问题;W八章全文ȝ以及l束语?/div>
2、基本概忉|节介l了本文使用到的基本的NAT术语Q着重描qC适用于UDP和TCP两种协议的通用的NATI越技术?/div>
2.1、NAT术语
本文l大部分术语和分cL自于RFC 2663定义Q另外一些来自于较新的RFC 3489中的定义?/div>
理解session是很重要的。一个TCP或UDP的session endpoint是由一个IP地址Q端口号l成Q每个session是由两个session endpoint构成。从内网节点的角度来看,一个session?部分l成分别为:本地IPQ本地端口,q端IPQ远端端口。session的方向通常代表了数据包的初始流动的方向Q对于TCP来说是SYN包的向Q对于UDP来说是W一个用h据包的流向?/div>
NAT有很多种Q但最普遍的一U类型叫?#8220;传统”NATQ或?#8220;向外”NAT。他们在内网和公|之间提供了一?#8220;不对U?#8221;桥的映射?#8220;向外”NAT在默认情况下只允许向外的sessionI越NATQ?/div>
从外向内的的数据包都会被丢弃掉,除非NAT讑֤事先已经定义了这些从外向内的数据包是已存在的内网session的一部分?/div>
“外向”NAT会造成p2p协议的乱,因ؓ当p2p的双方决定向在不同NAT后面的对方开始通信的时候,无论哪一方试囑ֈ始化一个sessionQ另一方的NAT都会拒绝q个h。NATI越的核心思想是让p2p的双方的NAT看上去都?#8220;向外”的NAT?/div>
“向外”NAT有两U类型:Q?Q?#8220;基础”NATQ该NAT只{换IP地址Q不转换端口受(2QNAPT(Network Address/Port Translation)NAPT转换整个session endpoints。由于NAPT允许内网的多个节炚w过׃n的方式用同一个的公共的IP地址Q因此,支持NAPT的NAT讑֤才会来多。尽本文通篇讨论的内定w是基于支持NAPT的NAT讑֤的,但这些规律和技术同样适用?#8220;基础”NAT?/div>
2.2 转发方式
最可靠但同时也是效率最低的p2pI越NATq行通信的方法是采用cMC/S方式的{发。假定两个节点A和B每个节点都有向外的TCP或UDP q接Q联入公q已知服务器SQS的公|IP地址?8.181.0.31Q端口号?234Q如?所C),每个客户端位于不同的U有内网中,q且它们的NAT讑֤妨碍了客L之间直接的p2pq接。做为对直连Ҏ的替代方案,两个客户端可以利用公q服务器Sq行消息的{发。例如,AZ消息送给 BQA只需消息发lSQ然后由S转发lBQ这一q程用A与B事先与S建立好的q接?/div>
转发方式通常只能在双方客L都连接到服务器的时候有效。这U方式的~点在于Q它假定服务器的处理能力和网l带宽以及通信延迟都是理想的情况下Q不会受到客L个数的媄响。但是,׃没有其它的方法能够像转发方式那样Q可以穿现存的所有NAT讑֤Q因此在构徏高可靠性的p2ppȝ的时候,通过服务器{发的方式依旧是一个非常有用的保证pȝ可靠性的Ҏ。TURN协议定义了如何实现安全的转发方式?/div>
2.3 反向q接方式