首先,我们要明确一下,通过socket发送数据之后(调用发送方法),数据并不是会直接发送到接收方。
整个过程简略图如下:

最终数据发送是要通过网卡的,但是socket没法直接将数据发送到网卡,所以只能先将数据发送到操作系统数据发送缓冲区。
然后网卡从数据发送缓冲区中获取数据,再发送到接收方。
socket接收数据,也不是直接从对端获取的。
整个过程简略图如下:

首先接收方机器网卡接收到发送方的数据后,先将数据保存到操作系统接收缓冲区。
client端感知到操作系统缓冲区的数据后,主动调用接收数据的方法来获取数据。
首先,这两个区域是每一个Socket连接都有的。本质上而言,就是内核中的两块内存空间,socket创建完成后,这两块内存空间就开辟出来了。
至于其作用,如上图所示,主要是为了做数据缓冲区用的。
一般情况下,我们不需要主动设置(当然,也不建议主动设置这两个区域)。
貌似在我们的代码中,从来没有主动设置过这两个区域。
既然我们没有主动设置过,那么平时我们怎么知道,这两块区域的占用内存大小是多少呢。
就java技术栈而言,我们可以通过获取Socket的属性来获取
Socket socket = new Socket();
int sendBufferSize = socket.getSendBufferSize(); // 发送缓冲区
int receiveBufferSize = socket.getReceiveBufferSize(); // 接收缓冲区
笔者在Windows电脑上测试,默认输出都是65536,也就是64KB。
Linux系统下,我们可以通过查看参数来获取默认值
[root@cfs_netstorage core]# cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 131072 //写缓冲区大小 第一个表示最小值,第二个表示默认值,第三个表示最大值。
[root@cfs_netstorage core]# cat /proc/sys/net/ipv4/tcp_rmem
4096 87380 174760 //读缓冲区大小 第一个表示最小值,第二个表示默认值,第三个表示最大值。
肯定是可以的。
在java栈中,我们可以通过如下方式设置
Socket socket = new Socket();
socket.setSendBufferSize(size); // 设置写缓冲区大小
socket.setReceiveBufferSize(size); // 设置读缓冲区大小
但是这个设置是不能超过系统设置参数的。
以下解释来自于: 高性能网络编程7--tcp连接的内存使用_陶辉的博客-CSDN博客
SO_SNDBUF、SO_RCVBUF都是个体化的设置,即,只会影响到设置过的连接,而不会对其他连接生效。SO_SNDBUF表示这个连接上的内核写缓存上限。
实际上,进程设置的SO_SNDBUF也并不是真的上限,在内核中会把这个值翻一倍再作为写缓存上限使用。
我们不需要纠结这种细节,只需要知道,当设置了SO_SNDBUF时,就相当于划定了所操作的TCP连接上的写缓存能够使用的最大内存。
然而,这个值也不是可以由着进程随意设置的,它会受制于系统级的上下限,当它大于上面的系统配置wmem_max(net.core.wmem_max)时,
将会被wmem_max替代(同样翻一倍);而当它特别小时,例如在2.6.18内核中设计的写缓存最小值为2K字节,此时也会被直接替代为2K。
SO_RCVBUF表示连接上的读缓存上限,与SO_SNDBUF类似,它也受制于rmem_max配置项,实际在内核中也是2倍大小作为读缓存的使用上限。
SO_RCVBUF设置时也有下限,同样在2.6.18内核中若这个值小于256字节就会被256所替代。
以下解释来自于: 高性能网络编程7--tcp连接的内存使用_陶辉的博客-CSDN博客
TCP连接所用内存主要由读写缓存决定,而读写缓存的大小只与实际使用场景有关,在实际使用未达到上限时,SO_SNDBUF、SO_RCVBUF是不起任何作用的。
对读缓存来说,接收到一个来自连接对端的TCP报文时,会导致读缓存增加,当然,如果加上报文大小后读缓存已经超过了读缓存上限,那么这个报文会被丢弃从而读缓存大小维持不变。
什么时候读缓存使用的内存会减少呢?当进程调用read、recv这样的方法读取TCP流时,读缓存就会减少。
因此,读缓存是一个动态变化的、实际用到多少才分配多少的缓冲内存,当这个连接非常空闲时,且用户进程已经把连接上接收到的数据都消费了,那么读缓存使用内存就是0。
写缓存也是同样道理。当用户进程调用send或者write这样的方法发送TCP流时,就会造成写缓存增大。当然,如果写缓存已经到达上限,那么写缓存维持不变,向用户进程返回失败。
而每当接收到TCP连接对端发来的ACK确认了报文的成功发送时,写缓存就会减少,这是因为TCP的可靠性决定的,发出去报文后由于担心报文丢失而不会销毁它,
可能会由重发定时器来重发报文。因此,写缓存也是动态变化的,空闲的正常连接上,写缓存所用内存通常也为0。
因此,只有当接收网络报文的速度大于应用程序读取报文的速度时,可能使读缓存达到了上限,这时这个缓存使用上限才会起作用。
所起作用为:丢弃掉新收到的报文,防止这个TCP连接消耗太多的服务器资源。
同样,当应用程序发送报文的速度大于接收对方确认ACK报文的速度时,写缓存可能达到上限,从而使send这样的方法失败,内核不为其分配内存。
答案是:不推荐。
我们考虑以下实际场景:
当连接到服务端的连接比较少时,我们期望设置该连接的读写缓冲区大小为系统缓存上限,这样便可以充分利用网络资源;
当连接到服务端的连接比较多时,这时系统内存为稀缺资源了,为了支持更多的连接,便需要减小每个连接的读写缓冲区大小;
为了支持这种场景,Linux操作系统实现了自动调整分配内存的功能。由以下参数来决定
net.ipv4.tcp_moderate_rcvbuf = 1 # 默认tcp_moderate_rcvbuf配置为1,表示打开了TCP内存自动调整功能。若配置为0,这个功能将不会生效
但是,如果我们主动配置了socket读写缓冲区大小,那么这个自动分配内存的功能就不再生效。
需要先明确下:
发送缓冲区>=发送窗口
如下图所示:(图片来自: TCP缓存区与窗口的关系_ccc_yxc的博客-CSDN博客_发送窗口和发送缓存 )

发送窗口只是发送缓冲区的一部分,已经被确认的数据则会从发送缓冲区中删除。
接收缓冲区>=接收窗口
如下图所示:(图片来自: TCP缓存区与窗口的关系_ccc_yxc的博客-CSDN博客_发送窗口和发送缓存 )

接收缓冲区中存放的数据,一旦被应用程序读取到之后,则会从缓冲区中删除。
socket tcp缓冲区大小的默认值、最大值_zzd2018的博客-CSDN博客_socket接收缓冲区最大值
高性能网络编程7--tcp连接的内存使用_陶辉的博客-CSDN博客
TCP缓存区与窗口的关系_ccc_yxc的博客-CSDN博客_发送窗口和发送缓存
我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass
我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
我有一个非常简单的RubyRack服务器,例如:app=Proc.newdo|env|req=Rack::Request.new(env).paramspreq.inspect[200,{'Content-Type'=>'text/plain'},['Somebody']]endRack::Handler::Thin.run(app,:Port=>4001,:threaded=>true)每当我使用JSON对象向服务器发送POSTHTTP请求时:{"session":{"accountId":String,"callId":String,"from":Object,"headers":
我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=
SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手
s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成
我很难理解Ruby中sender和receiver的实际含义。它们一般是什么意思?到目前为止,我只是将它们理解为方法调用和获取其返回值的调用。但是,我知道我的理解还远远不够。谁能给我一个Ruby中发送者和接收者的具体解释? 最佳答案 面向对象中的一个核心概念是消息传递和早期概念化,这在很大程度上借鉴了计算的Actor模型。艾伦·凯(AlanKay)创造了面向对象一词并发明了最早的OO语言之一SmallTalk,他拥有voicedregretatusingatermwhichputthefocusonobjectsinsteadofo
假设我们有A、B、C类。Adefself.inherited(sub)#metaprogramminggoeshere#takeclassthathasjustinheritedclassA#andforfooclassesinjectprepare_foo()as#firstlineofmethodthenrunrestofthecodeenddefprepare_foo#=>prepare_foo()neededhere#somecodeendendBprepare_foo()neededhere#somecodeendend如您所见,我正在尝试将foo_prepare()调用注入
如果我必须在一个HTTP请求中发送一堆post参数,所有这些参数都具有相同的名称,我该如何构建要发布的data对象?想象一个带有一些复选框的表单,它们都具有相同的name属性但具有不同的值(如果它们被选中):我想用ruby构建它(但它需要根据在表单上选择的内容动态创建):data={"color"=>"red","color"=>"green","color"=>"blue"}然后将数据发送到某个URL:Net::HTTP.post_form(url,data)我无法控制接收端,所以我必须发送它期望接收的参数。怎么办? 最佳答案