竭诚为您提供优质文档/双击可除
socket接收函数send
篇一:socketsend函数和recv函数详解
intsend(socKeTs,constcharFAR*buf,intlen,intflags);不论是客户还是服务器应用程序都用send函数来向Tcp连接的另一端发送数据。
客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
该函数的第一个参数指定发送端套接字描述符;
第二个参数指明一个存放应用程序要发送数据的缓冲区;
第三个参数指明实际要发送的数据的字节数;
第四个参数一般置0。
这里只描述同步socket的send函数的执行流程。当调用该函数时,send先比较待发送数据的
长度len和套接字s的发送缓冲的长度,如果len大于s的发送缓冲区的长度,该函数返回socKeT_eRRoR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果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)
要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个socket函数就会返回socKeT_eRRoR。(每一个除send外的socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该socket函数就返回socKeT_eRRoR)
注意:在unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个sIgpIpe信号,进程对该信号的默认处理是进程终止。
通过测试发现,异步socket的send函数在网络刚刚断开时还能发送返回相应的字节数,同时使用select检测也是可写的,但是过几秒钟之后,再send就会出错了,返回-1。select也不能检测出可写了。
recv函数
intrecv(socKeTs,charFAR*buf,intlen,intflags);
不论是客户还是服务器应用程序都用recv函数从Tcp连接的另一端接收数据。
该函数的第一个参数指定接收端套接字描述符;
第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第三个参数指明buf的长度;
第四个参数一般置0。
这里只描述同步socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回socKeT_eRRoR,如果s的发送缓冲中没有数据或者数据被协议成功
通过测试发现,异步socket的send函数在网络刚刚断开时还能发送返回相应的字节数,同时使用select检测也是可写的,但是过几秒钟之后,再send就会出错了,返回-1。select也不能检测出可写了。
recv函数
intrecv(socKeTs,charFAR*buf,intlen,intflags);
不论是客户还是服务器应用程序都用recv函数从Tcp连接的另一端接收数据。
该函数的第一个参数指定接收端套接字描述符;
第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第三个参数指明buf的长度;
第四个参数一般置0。
这里只描述同步socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回socKeT_eRRoR,如果s的发送缓冲中没有数据或者数据被协议成功
发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回socKeT_eRRoR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
注意:在unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个sIgpIpe信号,进程对该信号的默认处理是进程终止。
/*******************************************************************************/
intsend(
socKeTs,
constcharFAR*buf,
intlen,
intflags
注意:在unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个sIgpIpe信号,进程对该信号的默认处理是进程终止。
/*******************************************************************************/
intsend(
socKeTs,
constcharFAR*buf,
intlen,
intflags
);
请问send函数每次最多可以发送多少数据?是int的最大值吗?
【sms88】:
不是int的最大值
【sms88】:
理论上视乎是一个窗口大小减去协议头的大小
【xxyyboy】:
不是,send有不指定长度的,有指定长度的。
intsend(
socKeTs,//连路
constcharFAR*buf,//buffer内容
intlen,//bufferlen
intflags//不知道
);
函数int只是表示发送成功失败的返回值,不表示多长
请问send函数每次最多可以发送多少数据?是int的最大值吗?
【sms88】:
不是int的最大值
【sms88】:
理论上视乎是一个窗口大小减去协议头的大小
【xxyyboy】:
不是,send有不指定长度的,有指定长度的。
intsend(
socKeTs,//连路
constcharFAR*buf,//buffer内容
intlen,//bufferlen
intflags//不知道
);
函数int只是表示发送成功失败的返回值,不表示多长
像你这个就是有buffer就发送
recv到的数据流可能是断断续续的,你要把他们放在一起然后解码。
【dodoboy】:
那也就是说如果buffer中的数据过大,我也只需要调用一次send函数,而低层到底是一次传输成功还是陆续传输我不用管了吗?
【mudunke775】:
recv到的数据流可能是断断续续的,你要把他们放在一起然后解码。
//////////////////////////////////////////////////////////////////
解码和合并包是低层做的,不用你自己解码吧?
【unsigned】:
send分为阻塞和非阻塞,阻塞模式下,如果正常的话,会直到把你所需要发送的数据发完再返回;非阻塞,会根据你的socket在底层的可用缓冲区的大小,来将你的缓冲区当中的数据拷贝过去,有多大缓冲区就拷贝多少,缓冲区满了就立即返回,这个时候的返回值,只表示拷贝到缓冲区多少数据,但是并不代表发送多少数据,同时剩下的部分需要你再次调用send才会再一次拷贝到底层缓冲区。
recv到的数据流可能是断断续续的,你要把他们放在一起然后解码。
【dodoboy】:
那也就是说如果buffer中的数据过大,我也只需要调用一次send函数,而低层到底是一次传输成功还是陆续传输我不用管了吗?
【mudunke775】:
recv到的数据流可能是断断续续的,你要把他们放在一起然后解码。
//////////////////////////////////////////////////////////////////
解码和合并包是低层做的,不用你自己解码吧?
【unsigned】:
send分为阻塞和非阻塞,阻塞模式下,如果正常的话,会直到把你所需要发送的数据发完再返回;非阻塞,会根据你的socket在底层的可用缓冲区的大小,来将你的缓冲区当中的数据拷贝过去,有多大缓冲区就拷贝多少,缓冲区满了就立即返回,这个时候的返回值,只表示拷贝到缓冲区多少数据,但是并不代表发送多少数据,同时剩下的部分需要你再次调用send才会再一次拷贝到底层缓冲区。
【iambic】:
和write差不多。
【Arthur_】:
Youcanuse*setsockopt*toenlargethebuffer.
作为一个套接字,它拥有两个缓冲,接收数据缓冲和发送数据缓冲(此缓冲不同与你自己定义的缓冲),当有数据到达时,首先进入的就是接收数据缓冲,然后用户从这个缓冲中将数据读出来,这就是套接字接受的过程,这个缓冲的大小可以自己用setsocketopt()设定,同时操作系统对它有一个默认大小,如果对方在很短时间内发送大量数据到达这个套接子时,可能它没来得及接收完,因此接收缓冲处于满的状态,再有数据来的时候就进不去了,因此对方的senD可能就返回错误,在对方发送的数据量很小时不会出现这种情况,当数据量很大时,情况就很明显了,很容易造成收不到的情况。
同样,发送方的发送缓冲也有相对应的问题。
解决这个问题的办法有几种:
1:利用setsocketopt()函数将接收方套接子接收缓冲设为足够大小;2:在发送方进行数据发送时判断发送是否成功,如果不成功重发;
和write差不多。
【Arthur_】:
Youcanuse*setsockopt*toenlargethebuffer.
作为一个套接字,它拥有两个缓冲,接收数据缓冲和发送数据缓冲(此缓冲不同与你自己定义的缓冲),当有数据到达时,首先进入的就是接收数据缓冲,然后用户从这个缓冲中将数据读出来,这就是套接字接受的过程,这个缓冲的大小可以自己用setsocketopt()设定,同时操作系统对它有一个默认大小,如果对方在很短时间内发送大量数据到达这个套接子时,可能它没来得及接收完,因此接收缓冲处于满的状态,再有数据来的时候就进不去了,因此对方的senD可能就返回错误,在对方发送的数据量很小时不会出现这种情况,当数据量很大时,情况就很明显了,很容易造成收不到的情况。
同样,发送方的发送缓冲也有相对应的问题。
解决这个问题的办法有几种:
1:利用setsocketopt()函数将接收方套接子接收缓冲设为足够大小;2:在发送方进行数据发送时判断发送是否成功,如果不成功重发;
3:要求接收方收到数据后给发送方回应,发送方只在收到回应后才发送下一条数据。
篇二:socket函数readwritesend和recv
socket函数readwritesend和recv20XX-09-2113:10:28阅读492评论0字号:大中小
一旦,我们建立好了tcp连接之后,我们就可以把得到的fd当作文件描述符来使用。由此网络程序里最基本的函数就是read和write函数了。
ssize_twrite(intfd,constvoid*buf,size_tnbytes);
write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1.并设置errno变量.在网络程序中,当我们向套接字文件描述符写时有两可能.
1)write的返回值大于0,表示写了部分或者是全部的数据.这样我们用一个while循环来不停的写入,但是循环过程中的buf参数和nbyte参数得由我们来更新。也就是说,网络写函数是不负责将全部数据写完之后在返回的。
2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理.
如果错误为eInTR表示在写的时候出现了中断错误.
如果为epIpe表示网络连接出现了问题(对方已经关闭了连接).
为了处理以上的情况,我们自己编写一个写函数来处理这几种情况.
篇二:socket函数readwritesend和recv
socket函数readwritesend和recv20XX-09-2113:10:28阅读492评论0字号:大中小
一旦,我们建立好了tcp连接之后,我们就可以把得到的fd当作文件描述符来使用。由此网络程序里最基本的函数就是read和write函数了。
ssize_twrite(intfd,constvoid*buf,size_tnbytes);
write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1.并设置errno变量.在网络程序中,当我们向套接字文件描述符写时有两可能.
1)write的返回值大于0,表示写了部分或者是全部的数据.这样我们用一个while循环来不停的写入,但是循环过程中的buf参数和nbyte参数得由我们来更新。也就是说,网络写函数是不负责将全部数据写完之后在返回的。
2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理.
如果错误为eInTR表示在写的时候出现了中断错误.
如果为epIpe表示网络连接出现了问题(对方已经关闭了连接).
为了处理以上的情况,我们自己编写一个写函数来处理这几种情况.
intmy_write(intfd,void*buffer,intlength)recv函数
{
intbytes_left;
intwritten_bytes;
char*ptr;
ptr=buffer;
bytes_left=length;
while(bytes_left>0)
{
/*开始写*/
written_bytes=write(fd,ptr,bytes_left);
if(written_bytes {
if(errno==eInTR)/*中断错误我们继续写*/
written_bytes=0;
else/*其他错误没有办法,只好撤退了*/
{
intbytes_left;
intwritten_bytes;
char*ptr;
ptr=buffer;
bytes_left=length;
while(bytes_left>0)
{
/*开始写*/
written_bytes=write(fd,ptr,bytes_left);
if(written_bytes {
if(errno==eInTR)/*中断错误我们继续写*/
written_bytes=0;
else/*其他错误没有办法,只好撤退了*/
return(-1);
}
bytes_left-=written_bytes;
ptr+=written_bytes;/*从剩下的地方继续写*/
}
return(0);
}
读函数read
ssize_tread(intfd,void*buf,size_tnbyte)
read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误.如果错误为eInTR说明读是由中断引起的,如果是econnResT表示网络连接出了问题.和上面一样,我们也写一个自己的读函数.
intmy_read(intfd,void*buffer,intlength)
{
intbytes_left;
}
bytes_left-=written_bytes;
ptr+=written_bytes;/*从剩下的地方继续写*/
}
return(0);
}
读函数read
ssize_tread(intfd,void*buf,size_tnbyte)
read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误.如果错误为eInTR说明读是由中断引起的,如果是econnResT表示网络连接出了问题.和上面一样,我们也写一个自己的读函数.
intmy_read(intfd,void*buffer,intlength)
{
intbytes_left;
intbytes_read;
char*ptr;
bytes_left=length;
while(bytes_left>0)
{
bytes_read=read(fd,ptr,bytes_read);
if(bytes_read {
if(errno==eInTR)
bytes_read=0;
else
return(-1);
}
elseif(bytes_read==0)
break;
bytes_left-=bytes_read;
char*ptr;
bytes_left=length;
while(bytes_left>0)
{
bytes_read=read(fd,ptr,bytes_read);
if(bytes_read {
if(errno==eInTR)
bytes_read=0;
else
return(-1);
}
elseif(bytes_read==0)
break;
bytes_left-=bytes_read;
ptr+=bytes_read;
}
return(length-bytes_left);
}
数据的传递
有了上面的两个函数,我们就可以向客户端或者是服务端传递数据了.比如我们要传递一个结构.可以使用如下方式
/*客户端向服务端写*/
structmy_structmy_struct_client;
write(fd,(void*)
/*服务端的读*/
charbuffer[sizeof(structmy_struct)];
struct*my_struct_server;
read(fd,(void*)buffer,sizeof(structmy_struct));
my_struct_server=(structmy_struct*)buffer;
}
return(length-bytes_left);
}
数据的传递
有了上面的两个函数,我们就可以向客户端或者是服务端传递数据了.比如我们要传递一个结构.可以使用如下方式
/*客户端向服务端写*/
structmy_structmy_struct_client;
write(fd,(void*)
/*服务端的读*/
charbuffer[sizeof(structmy_struct)];
struct*my_struct_server;
read(fd,(void*)buffer,sizeof(structmy_struct));
my_struct_server=(structmy_struct*)buffer;
在网络上传递数据时我们一般都是把数据转化为char类型的数据传递.接收的时候也是一样的注意的是我们没有必要在网络上传递指针(因为传递指针是没有任何意义的,我们必须传递指针所指向的内容)
6.1recv和send
recv和send函数提供了和read和write差不多的功能.不过它们提供了第四个参数来控制读写操作.
intrecv(intsockfd,void*buf,intlen,intflags)
intsend(intsockfd,void*buf,intlen,intflags)
前面的三个参数和read,write一样,第四个参数可以是0或者是以下的组合
_______________________________________________________________
|msg_DonTRouTe|不查表|
|msg_oob|接受或者发送带外数据|
|msg_peeK|查看数据,并不从系统缓冲区移走数据|
|msg_wAITALL|等待所有数据|
|--------------------------------------------------------------|
6.1recv和send
recv和send函数提供了和read和write差不多的功能.不过它们提供了第四个参数来控制读写操作.
intrecv(intsockfd,void*buf,intlen,intflags)
intsend(intsockfd,void*buf,intlen,intflags)
前面的三个参数和read,write一样,第四个参数可以是0或者是以下的组合
_______________________________________________________________
|msg_DonTRouTe|不查表|
|msg_oob|接受或者发送带外数据|
|msg_peeK|查看数据,并不从系统缓冲区移走数据|
|msg_wAITALL|等待所有数据|
|--------------------------------------------------------------|
msg_DonTRouTe:是send函数使用的标志.这个标志告诉Ip.目的主机在本地网络上面,没有必要查表.这个标志一般用网络诊断和路由程序里面.
msg_oob:表示可以接收和发送带外的数据.关于带外数据我们以后会解释的.
msg_peeK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容.这样下次读的时候,仍然是一样的内容.一般在有多个进程读写数据时可以使用这个标志.
msg_wAITALL是recv函数的使用标志,表示等到所有的信息到达时才返回.使用这个标志的时候recv回一直阻塞,直到指定的条件满足,或者是发生了错误.1)当读到了指定的字节时,函数正常返回.返回值等于len2)当读到了文件的结尾时,函数正常返回.返回值小于len3)当操作发生错误时,返回-1,且设置错误为相应的错误号(errno)
msg_nosIg
nALisaflagusedbysend()insomeimplementationsoftheberkeleysockets.Thisflagrequeststhattheimplementationdoesnottosendasignalonerrorsonstreamorientedsocketswhentheotherendbreakstheconnection.Theerrorisstillreturnedasnormal.
ThoughitisinsomeberkelysocketsApIs(notablyLinux)itdoesnotexistinwhatsomerefertoasthereferenceimplementation,,whichinsteadusesasocketoptionso_nosIgpIpe.对于服务器端,我们可以使用这个标志。目的是不让其发送sIg_pIpe信号,导致程序退出。
msg_oob:表示可以接收和发送带外的数据.关于带外数据我们以后会解释的.
msg_peeK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容.这样下次读的时候,仍然是一样的内容.一般在有多个进程读写数据时可以使用这个标志.
msg_wAITALL是recv函数的使用标志,表示等到所有的信息到达时才返回.使用这个标志的时候recv回一直阻塞,直到指定的条件满足,或者是发生了错误.1)当读到了指定的字节时,函数正常返回.返回值等于len2)当读到了文件的结尾时,函数正常返回.返回值小于len3)当操作发生错误时,返回-1,且设置错误为相应的错误号(errno)
msg_nosIg
nALisaflagusedbysend()insomeimplementationsoftheberkeleysockets.Thisflagrequeststhattheimplementationdoesnottosendasignalonerrorsonstreamorientedsocketswhentheotherendbreakstheconnection.Theerrorisstillreturnedasnormal.
ThoughitisinsomeberkelysocketsApIs(notablyLinux)itdoesnotexistinwhatsomerefertoasthereferenceimplementation,,whichinsteadusesasocketoptionso_nosIgpIpe.对于服务器端,我们可以使用这个标志。目的是不让其发送sIg_pIpe信号,导致程序退出。
如果flags为0,则和read,write一样的操作.还有其它的几个选项,不过我们实际上用的很少,可以查看Linuxprogrammersmanual得到详细解释.
篇三:send、recv函数解析
socket使用Tcp协议时,send、recv函数解
析以及Tcp连接关闭的问题
socket使用Tcp协议时,send、recv函数解析以及Tcp连接关闭的问题
Tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据.
在阻塞模式下,send函数的过程是将应用程序请求发送的数据拷贝到发送缓存中发送并得到确认后再返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要大,那么send函数立即返回,同时向网络中发送数据;否则,send向网络发送缓存中不能容纳的那部分数据,并等待对端确认后再返回(接收端只要将数据收到接收缓存中,就会确认,并不一定要等待应用程序调用recv);
在非阻塞模式下,send函数的过程仅仅是将数据拷贝到协议栈的缓存区而已,如果缓存区可用空间不够,则尽能力的拷贝,返回成功拷贝的大小;如缓存区可用空间为0,则返回-1,同时设置err
篇三:send、recv函数解析
socket使用Tcp协议时,send、recv函数解
析以及Tcp连接关闭的问题
socket使用Tcp协议时,send、recv函数解析以及Tcp连接关闭的问题
Tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据.
在阻塞模式下,send函数的过程是将应用程序请求发送的数据拷贝到发送缓存中发送并得到确认后再返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要大,那么send函数立即返回,同时向网络中发送数据;否则,send向网络发送缓存中不能容纳的那部分数据,并等待对端确认后再返回(接收端只要将数据收到接收缓存中,就会确认,并不一定要等待应用程序调用recv);
在非阻塞模式下,send函数的过程仅仅是将数据拷贝到协议栈的缓存区而已,如果缓存区可用空间不够,则尽能力的拷贝,返回成功拷贝的大小;如缓存区可用空间为0,则返回-1,同时设置err
no为eAgAIn.
linux下可用sysctl-a|p_wmem查看系统默认的发送缓存大小:p_wmem=40961638481920
这有三个值,第一个值是socket的发送缓存区分配的最少字节数,第二个值是默认值(该值会被wmem_default覆盖),缓存区在系统负载不重的情况下可以增长到这个值,第三个值是发送缓存区空间的最大字节数(该值会被wmem_max覆盖).
根据实际测试,如果手工更改了p_wmem的值,则会按更改的值来运行,否则在默认情况下,协议栈通常是按wmem_default和wmem_max的值来分配内存的.
应用程序应该根据应用的特性在程序中更改发送缓存大小:
socklen_tsendbuflen=0;
socklen_tlen=sizeof(sendbuflen);
getsockopt(clientsocket,soL_socKeT,so_snDbuF,(void*)printf("default,sendbuf:%d/n",sendbuflen);
sendbuflen=10240;
setsockopt(clientsocket,soL_socKeT,so_snDbuF,(void*)getsockopt(clientsocket,soL_socK
linux下可用sysctl-a|p_wmem查看系统默认的发送缓存大小:p_wmem=40961638481920
这有三个值,第一个值是socket的发送缓存区分配的最少字节数,第二个值是默认值(该值会被wmem_default覆盖),缓存区在系统负载不重的情况下可以增长到这个值,第三个值是发送缓存区空间的最大字节数(该值会被wmem_max覆盖).
根据实际测试,如果手工更改了p_wmem的值,则会按更改的值来运行,否则在默认情况下,协议栈通常是按wmem_default和wmem_max的值来分配内存的.
应用程序应该根据应用的特性在程序中更改发送缓存大小:
socklen_tsendbuflen=0;
socklen_tlen=sizeof(sendbuflen);
getsockopt(clientsocket,soL_socKeT,so_snDbuF,(void*)printf("default,sendbuf:%d/n",sendbuflen);
sendbuflen=10240;
setsockopt(clientsocket,soL_socKeT,so_snDbuF,(void*)getsockopt(clientsocket,soL_socK
eT,so_snDbuF,(void*)printf("now,sendbuf:%d/n",sendbuflen);
需要注意的是,虽然将发送缓存设置成了10k,但实际上,协议栈会将其扩大1倍,设为20k.
-------------------实例分析---------------
在实际应用中,如果发送端是非阻塞发送,由于网络的阻塞或者接收端处理过慢,通常出现的情况是,发送应用程序看起来发送了10k的数据,但是只发送了2k到对端缓存中,还有8k在本机缓存中(未发送或者未得到接收端的确认).那么此时,接收应用程序能够收到的数据为2k.假如接收应用程序调用recv函数获取了1k的数据在处理,在这个瞬间,发生了以下情况之一,双方表现为:
A.发送应用程序认为send完了10k数据,关闭了socket:
发送主机作为tcp的主动关闭者,连接将处于FIn_wAIT1的半关闭状态(等待对方的ack),并且,发送缓存中的8k数据并不清除,依然会发送给对端.如果接收应用程序依然在recv,那么它会收到余下的8k数据(这个前题是,接收端会在发送端FIn_wAIT1状态超时前收到余下的8k数据.),然后得到一个对端socket被关闭的消息(recv返回0).这时,应该进行关闭.
b.发送应用程序再次调用send发送8k的数据:
假如发送缓存的空间为20k,那么发送缓存可用空间为20-8=12k,大于请求发送的8k,所以sen
需要注意的是,虽然将发送缓存设置成了10k,但实际上,协议栈会将其扩大1倍,设为20k.
-------------------实例分析---------------
在实际应用中,如果发送端是非阻塞发送,由于网络的阻塞或者接收端处理过慢,通常出现的情况是,发送应用程序看起来发送了10k的数据,但是只发送了2k到对端缓存中,还有8k在本机缓存中(未发送或者未得到接收端的确认).那么此时,接收应用程序能够收到的数据为2k.假如接收应用程序调用recv函数获取了1k的数据在处理,在这个瞬间,发生了以下情况之一,双方表现为:
A.发送应用程序认为send完了10k数据,关闭了socket:
发送主机作为tcp的主动关闭者,连接将处于FIn_wAIT1的半关闭状态(等待对方的ack),并且,发送缓存中的8k数据并不清除,依然会发送给对端.如果接收应用程序依然在recv,那么它会收到余下的8k数据(这个前题是,接收端会在发送端FIn_wAIT1状态超时前收到余下的8k数据.),然后得到一个对端socket被关闭的消息(recv返回0).这时,应该进行关闭.
b.发送应用程序再次调用send发送8k的数据:
假如发送缓存的空间为20k,那么发送缓存可用空间为20-8=12k,大于请求发送的8k,所以sen
d函数将数据做拷贝后,并立即返回8192;
假如发送缓存的空间为12k,那么此时发送缓存可用空间还有12-8=4k,send()会返回4096,应用程序发现返回的值小于请求发送的大小值后,可以认为缓存区已满,这时必须阻塞(或通过select等待下一次socket可写的信号),如果应用程序不理会,立即再次调用send,那么会得到-1的值,在linux下表现为errno=eAgAIn.
c.接收应用程序在处理完1k数据后,关闭了socket:
接收主机作为主动关闭者,连接将处于FIn_wAIT1的半关闭状态(等待对方的ack).然后,发送应用程序会收到socket可读的信号(通常是select调用返回socket可读),但在读取时会发现recv函数返回0,这时应该调用close函数来关闭socket(发送给对方ack);
如果发送应用程序没有处理这个可读的信号,而是在send,那么这要分两种情况来考虑,假如是在发送端收到RsT标志之后调用send,send将返回-1,同时errno设为econnReseT表示对端网络已断开,但是,也有说法是进程会收到sIgpIpe信号,该信号的默认响应动作是退出进程,如果忽略该信号,那么send是返回-1,errno为epIpe(未证实);如果是在发送端收到RsT标志之前,则send像往常一样工作;
以上说的是非阻塞的send情况,假如send是阻塞调用,并且正好处于阻塞时(例如一次性发送
假如发送缓存的空间为12k,那么此时发送缓存可用空间还有12-8=4k,send()会返回4096,应用程序发现返回的值小于请求发送的大小值后,可以认为缓存区已满,这时必须阻塞(或通过select等待下一次socket可写的信号),如果应用程序不理会,立即再次调用send,那么会得到-1的值,在linux下表现为errno=eAgAIn.
c.接收应用程序在处理完1k数据后,关闭了socket:
接收主机作为主动关闭者,连接将处于FIn_wAIT1的半关闭状态(等待对方的ack).然后,发送应用程序会收到socket可读的信号(通常是select调用返回socket可读),但在读取时会发现recv函数返回0,这时应该调用close函数来关闭socket(发送给对方ack);
如果发送应用程序没有处理这个可读的信号,而是在send,那么这要分两种情况来考虑,假如是在发送端收到RsT标志之后调用send,send将返回-1,同时errno设为econnReseT表示对端网络已断开,但是,也有说法是进程会收到sIgpIpe信号,该信号的默认响应动作是退出进程,如果忽略该信号,那么send是返回-1,errno为epIpe(未证实);如果是在发送端收到RsT标志之前,则send像往常一样工作;
以上说的是非阻塞的send情况,假如send是阻塞调用,并且正好处于阻塞时(例如一次性发送
一个巨大的buf,超出了发送缓存),对端socket关闭,那么send将返回成功发送的字节数,如果再次调用send,那么会同上一样.
D.交换机或路由器的网络断开:
接收应用程序在处理完已收到的1k数据后,会继续从缓存区读取余下的1k数据,然后就表现为无数据可读的现象,这种情况需要应用程序来处理超时.一般做法是设定一个select等待的最大时间,如果超出这个时间依然没有数据可读,则认为socket已不可用.
发送应用程序会不断的将余下的数据发送到网络上,但始终得不到确认,所以缓存区的可用空
间持续为0,这种情况也需要应用程序来处理.
如果不由应用程序来处理这种情况超时的情况,也可以通过tcp协议本身来处理,具体可以查看sysctl项中的:
p_keepalive_intvl
p_keepalive_probes
p_keepalive_time
发送成功只是表示发到了内核socket缓冲区
此时如果close,正常情况会进入TIme_wAIT状态,在此状态,对端可以继续接收数据但是
D.交换机或路由器的网络断开:
接收应用程序在处理完已收到的1k数据后,会继续从缓存区读取余下的1k数据,然后就表现为无数据可读的现象,这种情况需要应用程序来处理超时.一般做法是设定一个select等待的最大时间,如果超出这个时间依然没有数据可读,则认为socket已不可用.
发送应用程序会不断的将余下的数据发送到网络上,但始终得不到确认,所以缓存区的可用空
间持续为0,这种情况也需要应用程序来处理.
如果不由应用程序来处理这种情况超时的情况,也可以通过tcp协议本身来处理,具体可以查看sysctl项中的:
p_keepalive_intvl
p_keepalive_probes
p_keepalive_time
发送成功只是表示发到了内核socket缓冲区
此时如果close,正常情况会进入TIme_wAIT状态,在此状态,对端可以继续接收数据但是
如果发送方的接收缓冲区还有未读数据,就会走异常close的途径,置RsT,立刻结束连接,没有TIme_wAIT状态。这时对端就收不全数据,报错:connectionresetbypeer。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论