socket编程---send函数&recv函数详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/We_are_family678/article/details/79844998
         <!--一个博主专栏付费入口结束-->
        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-d284373521.css">
                                    <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-d284373521.css">
            <div class="htmledit_views" id="content_views">
                                        <p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">socket函数创建了一个socket fd,看一下socket fd的概念:</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;"><span>套接字描述符</span>是一个<span>整数类型的值</span>。每个进程的进程空间里都有一个<span>套接字描述符</span>表,该表中存放着<span><span>套接字描述符</span>和套接字数据结构的对应关系</span>。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据<span>套接字描述符</span>就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个<span>套接字描述符</span>表但是套接字数据结构都是在操作系统的内核缓冲里。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">所以可以看到这个fd能帮你找到你所对应的缓冲区,一个fd对应两个缓冲区,一个接受缓冲区,一个发送缓冲区,而send函数和recv函数就是围绕着着两个缓冲区进行的。</p><h2 style="font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;font-size:21px;line-height:1.5;">一、send函数</h2><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">函数原型:</p><div class="cnblogs_code" style="color:rgb(69,69,69);border:1px solid rgb(204,204,204);font-family:'Courier New';"><pre style="font-size:14px;line-height:22px;font-family:'Courier New';"><span style="color:rgb(0,0,255);line-height:1.5;">int</span> send( SOCKET s,<span style="color:rgb(0,0,255);line-height:1.5;">char</span> *buf,<span style="color:rgb(0,0,255);line-height:1.5;">int</span> len,<span style="color:rgb(0,0,255);line-height:1.5;">int</span> flags );</pre></div><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">功能:不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">参数一:指定发送端套接字描述符;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">参数二:存放应用程序要发送数据的缓冲区;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">参数三:实际要发送的数据的字节数;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">参数四:一般置为0。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">同步Socket的send函数的执行流程,当调用该函数时,<span>send先比较待发送数据的长度len和套接字s的发送缓冲的长度</span>(因为待发送数据是要copy到套接字s的发送缓冲区的,注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里):</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">1.如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;【所以每次要自己切包咯??】</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">2.如果len小于或者等于s的发送缓冲区的长度,<span>那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,</span>如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么 send就比较s的发送缓冲区的剩余空间和len:</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">&nbsp; &nbsp; &nbsp; (i)如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">&nbsp; &nbsp; &nbsp; (ii)如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">3.如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">注意:send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,<span>但是此时这些数据并不一定马上被传到连接的另一端。</span>如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR)</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">二、recv函数</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">函数原型:int recv( SOCKET s, char *buf, int &nbsp;len, int flags)</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">功能:不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">参数一:指定接收端套接字描述符;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">参数二:指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">参数三:指明buf的长度;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">参数四 :一般置为0。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">&nbsp;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">同步Socket的recv函数的执行流程:当应用程序调用recv函数时,<span class="diigoHighlight id_f1981c2b98cd7b5b815f5eb4f47f7a33 type_0 yellow">rec<span><span>v先等待s的发送缓冲中的数据被协议传送完毕,(发送先)</span></span></span></p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">&nbsp;</p><hr style="color:rgb(69,69,69);font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;font-size:14px;"><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">&nbsp;</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">除此之外,还有一个非常经典的问题?<span>listen之后创建的套接字fd和accept创建的套接字fd是同一个吗?</span></p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">答案是否定的,四个套接字系统调用,socket()、bind()、connect()、accept(),可以完成一个完全五元相关的建立。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">socket()指定五元组中的<span>协议元</span>,它的用法与是否为客户或服务器、是否面向连接无关。</p><p style="font-size:14px;font-family:'Helvetica Neue', Helvetica, Verdana, Arial, sans-serif;">bind()指定五元组中的<span>本地二元,</span>即本地主机地址和端口号,其用法与是否面向连接有关:在服务器方,无论是否面向连接(TCP或UDP),均要调用bind()。</p><div class="cnblogs_code" style="color:rgb(69,69,69);border:1px solid rgb(204,204,204);font-family:'Courier New';"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy" style="line-height:1.5;"><a title="复制代码" style="border:none;"><img src="https://i-blog.csdnimg.cn/blog_migrate/69c5a8ac3fa60e0848d784a6dd461da6.gif" alt="复制代码" style="border:none;"></a></span></div><pre style="font-size:14px;line-height:22px;font-family:'Courier New';"><span style="line-height:1.5;">TCP: 创建一个socket,bind一个端口,listen连接的到来,调用accept接受连接请求,返回每个连接的句柄,然后就读写这些个句柄,就是用这些连接通信了。

UDP:创建一个Socket,bind一个端口,从端口上读不同客户端发来的数据,按数据中客户端地址端口往回发数据。

简单说就是一个在"连接"上通信,一个在套接字socekt上直接通信。

TCP多了listen和accept用来创建一个个用于通信的"连接","连接"里保存了对端地址,在这些"连接"上通信,底层还做了可靠性,数据顺序,重发,确认等。

UDP直接在socket上通信,每个数据包单独处理,按数据包中带来的发信地址往回发数据,这些数据到没到,顺序乱没乱没人管。有人在应用层自己实现重发确认等逻辑,即"可靠的UDP"概念。

复制代码

一个socket是由一个五元组来唯一标示的,即(协议,server_ip, server_port, client_ip, client_port)。只要该五元组中任何一个值不同,则其代表的socket就不同。这里忽略协议的区别,在同一协议的基础上,服务器端的listen socket的端口可以看成(server_ip, server_port, ***, ***),其中***是通配符,它跟任何一个client_ip, client_port值都不同,可以简单看成是(0,0)对,当然实现不是这样的。这样在服务器端accept之后,返回的连接socket的四元组就是(server_ip, server_port, client_ip, client_port),这里的client_ip,client_port因连接的客户端的不同而不同。所以accept返回的socket和listen socket是不同的,不同之处就在于四元组中的客户端ip和port,而服务器端的server_ip和server_port还是相同的,也就是accpet()函数返回的新的socket描述符的端口和listen端口是一样的。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值