send/recv与socket

2024-03-02 07:32
文章标签 socket send recv

本文主要是介绍send/recv与socket,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

send函数

不论是客户端还是服务器端应用程序都用send函数来向TCP连接的另一端发送数据。客户端程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

当调用send函数时,send先比较待发送数据的长度len和套接字s的发送缓冲区,如果len大于发送缓冲区的长度,该函数返回错误SOCKET_ERROR;如果len <= s的发送缓冲区,那么send先检查协议是否正在发送s的发送缓冲区中的数据,如果是就等待协议把数据发送完毕,如果协议还没有开始发送s的发送缓冲区中的数据或者s的发送缓冲区中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len > 剩余空间 send就一直等待协议把s的发送缓冲区中的数据发送完,如果len < 剩余空间 send就仅仅把buf中的数据copy到剩余空间里(send仅仅是把buf中的数据copy到s的发送缓冲区中,至于把数据传送到另一端是协议传的不是send)。如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误或者在等待协议传送数据时网络断开,那么send函数也返回SOCKET_ERROR。

要注意send函数把buf中的数据成功copy到s的发送缓冲区的剩余空间里,send函数就返回了,但是此时这些数据并不一定马上被传送到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个socket函数就会返回SOCKET_ERROR。但是注意的是:每一个除send以外的socket函数在执行的最开始总要等待套接字的发送缓冲区中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该socket函数就返回SOCKET_ERROR。

注意:在Unix系统之下,如果send在等待协议传送数据时网络断开,调用send的进程会接受到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

错误代码:

EBADF 参数s 非合法的socket处理代码。
EFAULT 参数中有一指针指向无法存取的内存空间
ENOTSOCK 参数s为一文件描述词,非socket。
EINTR 被信号所中断。
EAGAIN 此操作会令进程阻断,但参数s的socket为不可阻断。
ENOBUFS 系统的缓冲内存不足
ENOMEM 核心内存不足
EINVAL 传给系统调用的参数不正确。

recv函数

当调用recv函数时,recv函数先等待s的发送缓冲区中的数据都被协议传送完毕,如果协议在传送数据时出现了网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲区中没有数据或者协议成功发送完毕后,recv函数先检查s的接受缓冲区,如果s接收缓冲区中没有数据或者正在接收数据,那么recv函数久一直等待直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲区中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲区中的数据copy完毕。recv函数仅仅是拷贝数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv函数在copy时出错,返回SOCKET_ERROR,如果recv函数在等待协议接收数据时网络中断了,那么recv函数返回0。

注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

返回值说明

阻塞模式下recv会一直阻塞直到接收到数据,非阻塞模式下如果没有数据就会返回,不会阻塞着读,因此需要循环读取)。

返回说明:   

(1)成功执行时,返回接收到的字节数。

(2)若另一端已关闭连接则返回0,这种关闭是对方主动且正常的关闭

(3)失败返回-1,errno被设为以下的某个值   

特别地:返回值<0时并且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的,继续接收。

EAGAIN:套接字已标记为非阻塞,而接收操作被阻塞或者接收超时

EBADF:sock不是有效的描述词

ECONNREFUSE:远程主机阻绝网络连接

EFAULT:内存空间访问出错

EINTR:操作被信号中断

EINVAL:参数无效

ENOMEM:内存不足

ENOTCONN:与面向连接关联的套接字尚未被连接上

ENOTSOCK:sock索引的不是套接字

socket缓冲区

默认情况下socket是阻塞的。

send函数并不是直接将数据传输到网络中,而是负责将数据写入输出缓冲区,数据从输出缓冲区发送到目标主机是由TCP协议完成的。数据写入到输出缓冲区之后,send函数就可以返回了,数据是否发送出去,是否发送成功,何时到达目标主机,都不由它负责了,而是由协议负责。

recv函数也是一样的,它并不是直接从网络中获取数据,而是从输入缓冲区中读取数据。

输入输出缓冲区,系统会为每个socket都单独分配,并且是在socket创建的时候自动生成的。一般来说,默认的输入输出缓冲区大小为8K。套接字关闭的时候,输出缓冲区的数据不会丢失,会由协议发送到另一方;而输入缓冲区的数据则会丢失。

Socket数据发送与接收问题

数据的发送和接收是独立的,并不是发送方执行一次send,接收方就执行以此recv。recv函数不管发送几次,都会从输入缓冲区尽可能多的获取数据。如果发送方发送了多次信息,接收方没来得及进行recv,则数据堆积在输入缓冲区中,取数据的时候会都取出来。换句话说,recv并不能判断数据包的结束位置。

send函数:
在数据进行发送的时候,需要先检查输出缓冲区的可用空间大小,如果可用空间大小小于要发送的数据长度,则send会被阻塞,直到缓冲区中的数据被发送到目标主机,有了足够的空间之后,send函数才会将数据写入输出缓冲区。

TCP协议正在将数据发送到网络上的时候,输出缓冲区会被锁定(生产者消费者问题),不允许写入,send函数会被阻塞,直到数据发送完,输出缓冲区解锁,此时send才能将数据写入到输出缓冲区

要写入的数据大于输出缓冲区的最大长度的时候,要分多次写入,直到所有数据都被写到缓冲区之后,send函数才会返回。

recv函数:
函数先检查输入缓冲区,如果输入缓冲区中有数据,读取出缓冲区中的数据,否则的话,recv函数会被阻塞,等待网络上传来数据。如果读取的数据长度小于输出缓冲区中的数据长度,没法一次性将所有数据读出来,需要多次执行recv函数,才能将数据读取完毕。

 

参考:

https://blog.csdn.net/u010270148/article/details/53605339

https://www.cnblogs.com/sunziying/p/6501045.html

https://www.cnblogs.com/Berryxiong/p/6547510.html

https://www.cnblogs.com/msb-/articles/6042413.html

https://blog.csdn.net/u010871058/article/details/76147082

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

这篇关于send/recv与socket的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/765368

相关文章

Java Socket服务器端与客户端的编程步骤总结

一,InetAddress类: InetAddress类没有构造方法,所以不能直接new出一个对象; 可以通过InetAddress类的静态方法获得InetAddress的对象; InetAddress.getLocalHost(); InetAddress.getByName(""); 类主要方法: String - address.getHostName(); String - addre

VC环境下window网络程序:UDP Socket程序

最近在学Windows网络编程,正好在做UDPsocket的程序,贴上来: 服务器框架函数:              socket();    bind();    recfrom();  sendto();  closesocket(); 客户机框架函数:            socket();      recfrom();  sendto();  closesocket();

UVa 10820 Send a Table (Farey数列欧拉函数求和)

这里先说一下欧拉函数的求法 先说一下筛选素数的方法 void Get_Prime(){ /*筛选素数法*/for(int i = 0; i < N; i++) vis[i] = 1;vis[0] = vis[1] = 0;for(int i = 2; i * i < N; i++)if(vis[i]){for(int j = i * i; j < N; j += i)vis[j] =

socket()接口与内核协议栈的挂接

最近在看Brdige的代码,发现一个问题,同样的调用ioctl接口实现添加网桥、删除网桥、网桥增加网卡、网桥删除网卡等操作,一个应用层的接口,却通过两条路径实现,sock_ioctl和RTNETLINK(这本就不是一个级别的东西),而应用层的brctl-utils源码中并没有直接使用PF_NETLINK协议簇的情况,让我感到非常奇怪,因此想把glibc到系统调用,到协议簇注册,以及和VFS的关系再

linux下的Socket网络编程教程

套接字概念 Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。

socket函数接收发送详解

http://blog.csdn.net/g_brightboy/article/details/12854117 http://blog.csdn.net/liangkaiyang/article/details/5931901 send。。。 这里只描述同步Socket的send函数的执行流程。 当调用该函数时,send先比较待发送数据的长度

linux下socket常用函数

1、setprotoent(打开网络协议的数据文件) 相关函数  getprotobyname, getprotobynumber, endprotoent 表头文件  #include <netdb.h> 定义函数  void setprotoent (int stayopen); 函数说明      setprotoent()用来打开/etc/protocols,如果参数

udp网络通信 socket

套接字是实现进程间通信的编程。IP可以标定主机在全网的唯一性,端口可以标定进程在主机的唯一性,那么socket通过IP+端口号就可以让两个在全网唯一标定的进程进行通信。 套接字有三种: 域间套接字:实现主机内部的进程通信的编程 原始套接字:使用网络层或者数据链路层的接口进行编程,更难更底层,例如制作抓包等网络工具 网络套接字:实现用户通信的编程 udp网络通信 服务端server 分

c# Socket编程基础知识

这一篇文章,将图文并茂地介绍Socket编程的基础知识,我相信,如果你按照步骤做完实验,一定可以对Socket编程有更好地理解。 本文源代码,可以通过这里下载 http://files.cnblogs.com/chenxizhang/SocketWorkshop.rar   第一步:创建解决方案 第二步:创建服务端程序 这里可以选择“Console Application”这个类型,

C++与AS3中socket字节顺序

c++中默认字节顺序是大端,AS3中默认字节顺序是小端(见socket类中endian属性) 所以AS3与C++做socket通信时,无论发送还是接受C++消息时,需要修改字节顺序,例如 var bytes:ByteArray=new ByteArray; bytes.endian=Endian.LITTLE_ENDIAN;