非阻塞socket之send的使用---异步send

时间:2022-12-18 19:45:41

(1)错误现象:在发送小量数据的时候没有问题,一次发送完毕。但是当发送1008046个字节的时候,由于数据较大,系统一次约发送46336个字符,但是时而发送成功,时而中间断掉。现象是不稳定,并且返回Resource temporarily unavailable的errno,资源暂时不可用,显然这是由于没有约束没有限制的把数据抛给系统。显然是不对的。

需要发送的条件成立的时候才可以接着发送。后来查出原因,真是惭愧!

(2)概念:异步,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。
对于网络传输,可能传输时间比较长,传输距离足够长的话,用户能够感觉的到。或者是发送缓冲区一直处于忙的状态,这时候当send数据的时候,就会有一段时间的等待,等待对方的回复标识或是发送缓冲区中可以写入数据的标识。这个标识决定了下一次的send行为。
(3)同步send ---“傻傻的等”,除了程序的整体效率和资源利用率,时序能够强制保持正常,一般不会有问题。
(4)异步send
就是send函数成功发送自己的参数和函数地址之后,不必等待执行结果。而是向系统委托,让系统来监视这个执行过程,当send执行结束之后, 系统返回该信息,至于如何捕获该信息,一种常用的方法就是select。
这个过程大概如下:
设置socket为非阻塞式,其他过程省略。
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
socket函数的功能是creates an endpoint for communication and returns a descriptor.,可见是用于创建一个通信端点,并且返回一个文件描述符。
fcntl(socket_fd,F_SETFL,fcntl(socket_fd,F_GETFL,0) | O_NONBLOCK);


注意socket是否非阻塞,在单步调试的时候,connect有两种表现,当然需要一个连接不到的IP来看效果。
阻塞,conncet停在调用处一段时间后返回-1。非阻塞,connect直接过去不停留,显然需要另一种机制来获取连接的状态。方法和send检测socket发送是否成功相同,都是在检查socket的状态。
send数据,可以参考下面的程序结构:
int SendRequest(int  socket_fd,const char*send_buffer,long size)  
{
REG_DEBUG_PRINT("\n****************************Request******************************\n");
REG_DEBUG_PRINT("%s",send_buffer);
int ret = -1;
int Total = 0;
int lenSend = 0;

struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 500;
fd_set wset;
while(1)
{
FD_ZERO(&wset);
FD_SET(socket_fd, &wset);
if(select(socket_fd + 1, NULL, &wset, NULL, &tv) > 0)//3.5秒之内可以send,即socket可以写入
{
lenSend = send(socket_fd,send_buffer + Total,size -Total,0);
if(lenSend == -1)
{
ret = SEND_FAIL;
break;
}
Total += lenSend;
if(Total == size)
{
ret = OK;
break;
}
}
else //3.5秒之内socket还是不可以写入,认为发送失败
{
ret = SEND_FAIL;
break;
}
}
return ret;
}


关于send函数,摘之http://www.cnblogs.com/simonhaninmelbourne/archive/2012/08/05/2624104.html
int send( SOCKET s, const char FAR *buf, int len, int flags );
    不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
    该函数的第一个参数指定发送端套接字描述符;
    第二个参数指明一个存放应用程序要发送数据的缓冲区;
    第三个参数指明实际要发送的数据的字节数;
    第四个参数一般置0。
    这里只描述同步Socket的send函数的执行流程。当调用该函数时,
   (1)send先比较待发送数据的长度len和套接字s的发送缓冲的长度, 如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;
   (2)如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len
   (3)如果len大于剩余空间大小,send就一直等待协议把s的发送缓冲中的数据发送完
   (4)如果len小于剩余 空间大小,send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。
   如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。
   要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执 行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR)
注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
通过测试发现,异步socket的send函数在网络刚刚断开时还能发送返回相应的字节数,同时使用select检测也是可写的,但是过几秒钟之后,再send就会出错了,返回-1。select也不能检测出可写了