Socket 网络编程实践经验
目录
相关文章
NOTE:本文假设你已经对 Socket 的使用有一定的了解。
Socket 与 HTTP 的区别
首先通过对比法来了解两者不同的特性:
HTTP:超文本传输协议,首先它是一个协议,并且是基于 TCP/IP 协议(传输层)之上的应用层协议,要想通过 HTTP 来进行通信,首先需要双方建立起 TCP/IP 连接,因为TCP/IP 主要解决的问题是数据如何在网络中传输,而 HTTP 协议主要解决的问题是如何包装需要传输的数据。所以 HTTP 协议能够支持使用 Header 信息来详细规定浏览器与服务器之间的通信规则。HTTP 连接是基于 Request-Response 的非持久(短)连接,其连接的生命周期通过 Request 界定,一个 Request 一个 Response,此次 HTTP 连接的生命周期结束。对于这点在 HTTP 1.1 中进行了改进,支持 Keep-alive,允许在一个 HTTP 连接的生命周期中,可以发送多个 Request,接收多个 Response,但由 Request 来界定生命周期的本质没有改变。
Socket:首先需要注意的是 Socket 不是一种协议,而是一种调用接口(API)或称之为 TCP/IP 的基本操作单元。Socket 实际上是对 TCP/IP 协议的封装,通过 Socket,就能够应用 TCP/IP 协议来建立连接并传输数据。Socket 连接是持久化(长)连接,这种连接的生命周期能够自主控制,也就是说客户端和服务器端一旦建立连接后,理想状态下连接会持久存在,直到一方自动发出断开指令。
生产实践考虑
下面列出在生产项目中应用 Socket 所需要注意的几点:
- 网络断开重连问题
- 连接会话和身份认证问题
- 同步和异步问题
- 数据缓存问题
- 完全断开连接问题
网络断开重连问题
的确,Socket 连接在理想的网络环境下是持久的长连接,但实际网络环境是复杂的,网络抖动、路由宕机等各种网络问题都会导致 Socket 连接被动断开。而且 Socket 没有提供「自动重连」的机制,所以解决网络断开重连问题,是 Socket 程序稳定性的重要保证。
思路:在 发送 和 接收 前检查 Socket 连接是否依然生效,若不生效,则重新建立 Socket 连接。
那么首先需要解决的是:如何判断 Socket 连接状态是否 ACTIVE?
NOTE:一般的来说,「判断 Socket 连接状态是否 ACTIVE」都是服务端的功能需求,因为服务端需要以此来作为是否回收连接资源的依据。而客户端则无需特别在意,因为即便断开了连接也只需捕获异常、重新连接、重新发送即可。
Heartbeat 心跳机制
别名定义:
- 客户端 Socket == cli-socket
- 服务端 Socket == ser-socket
一般的 Socket 应用程序逻辑中,ser-socket 应该能够感知到 cli-socket 的断开,并且执行相应的断开逻辑处理,释放相应的 Socket 连接资源。但实际是,ser-socket 无法有效的区分 cli-socket 是处于长时间空闲还是处于 offline 的状态,所以也无法确定 cli-socket 的连接是否已经断开。为解决这个问题,程序员所提出的思路就是,屏蔽「长时间空闲」的场景,让 cli-socket 看起来始终是忙碌的(不断发送「无用包」),直到其静默即表示连接断开。
这就是较为通用的用于保证连接质量的心跳机制,而 cli-socket 发送的无用包也称之为心跳包。所谓心跳包就是 cli-socket 定时发送简单的协议信息给 ser-socket,以此让 ser-socket 知道 cli-socket 依旧 online。相对的, ser-socket 就会认为 cli-socket 已经断开。注意,发包方可以是 cli-socket 也可以是 ser-socket,但出于效率的考虑(减轻服务器压力),一般由 cli-socket 承担。如果是流式 Socket(for TCP),则使用 send 发出;如果是数据报式 Socket(for UDP),则使用 sendto 发出。还有一点需要说明,心跳包实际是一个自定义协议包,由开发者制定,并在 cli-socket 和 ser-socket 中遵守。
NOTE:如果仅为了确定 ser-socket 是否 online,可以用 TCP 协议自带的心跳包,应用 socket.socket.setsockopt 的 SO_KEEPALIVE 属性,来设置发包时间间隔。SO_KEEPALIVE 是操作系统的底层机制,用于维护每一个 TCP 连接。但SO_KEEPALIVE 并不能用于替代心跳机制,因为其仅能确保 ser-socket 一方的连接状态。
使用非阻塞模式下的 select 函数进行 Socket 连接检查
以异步(非阻塞)模式建立连接 s.setblocking(0),如果 select 函数返回的值为 1 (表示 Socket 可读),但使用 recv 函数读取到的数据长度为 0,并且 errno != EINTR and errno != EAGAIN,则说明该 Socket 已经断开
NOTE:需要注意的是,在非阻塞模式下,即便 recv 函数的返回值小于等于 0,依旧不足以证明问题。此时还需要继续判断 errno ?= EINTR,如果 Yes,则说明此次 recv 是由于程序接收到 EINTR 中断信号后返回的,Socket 连接仍然正常。除此之外,如果 write 写的太快,很有可能会把 Buffer 写满,这时的 errno == EAGAIN。根据实际需要,如果 errno == EAGAIN 的话,建议重试几次。当然,Read 也有类似的情况。
会话过期问题
如果服务端程序应用了 Session 机制,那么在实现客户端程序时除了需要考虑 Socket 连接的问题之外,还需要考虑 Session 是否过期的问题。
在 发送 和 接收 前首先检查连接是否有效,然后检查会话是否过期:
- 连接失效:则重新建立连接,并且重新创建 session
- 连接有效,但会话过期:则重新创建 session
- 连接有效,会话有效:PASS
同步还是异步问题
选择同步还是异步模式是非常重要的,使用了错误的连接模式将无法达到预期效果。例如高并发需求没能达到,例如程序的稳定性没能提高等等,如何进行选择需要结合实际的应用场景:
- 在高并发且不关注执行结果的场景中使用异步模式。
- 在对程序执行的稳定性,对执行结果响应的准确性都有很高要求的场景下使用同步模式,并且需要保证一次 send 和 recv 的原子性。
NOTE:对于后者,应该尽可以能仅保证 send 和 recv 原子操作是同步的,以此来优化效率。
数据缓存问题
Socket 的 recv 具有缓存功能,如果其中一方发送的数据量超过了另一方 recv 所允许一次接受的最大数据量,数据会被截短,并将剩余的数据缓冲在接收端。再次调用 recv 时,剩余的数据会从缓冲区取出并删除。这一特性与 HTTP 连接方式有很大区别,表示 Socket 连接无法像 HTTP 连接那般,一次 Request 对应的一次 Response 就能完成一次操作单元,而是很可能需要任意次的 send 以及任意次的 recv 才能完成。换句话说就是,需要开发者来保证 send 和 recv 的完整性,你需要手动的整理、组合出完整的发送和响应结果数据。经常的,为了得到一个完整的响应结果可能需要进行多次 recv。
完全断开连接问题
调用 Socket 的 close 函数并不会马上断开 Socket 连接,一般的我们会在 close 之前调用 shutdown 函数来确保连接会被正常关闭。而且 shutdown 提供了多种不同的关闭方式:
- SHUT_RD:关闭读,不能使用 read/recv
- SHUT_WR:关闭写,不能使用 send/write
- SHUT_RDWR:关闭读写,不能使用 send/write/recv/read
NOTE:在客户端程序中,一般我们会选择使用 SHUT_WR 方式,立即停止写操作,但可以继续将响应数据读完。而在服务端程序中一般会选择 SHUT_RD 模式,立即停止对客户端请求的读取,但会继续完成响应。当然了,在某些对精度要求不要的场景中,SHUT_RDWR 是不错的选择。
Socket 网络编程实践经验的更多相关文章
- Java之Socket网络编程实践
转自:http://my.oschina.net/leejun2005/blog/104955#comments 一.TCP/IP协议 既然是网络编程,涉及几个系统之间的交互,那么首先要考虑的是如何准 ...
- socket网络编程实践要点
1.创建udp的socket句柄 // 当host_port为0时,则表示让操作系统自动分配 bool createUdpSocket(string host_ip,unsigned short ho ...
- socket网络编程中read与recv区别
socket网络编程中read与recv区别 1.read 与 recv 区别 read 原则: 数据在不超过指定的长度的时候有多少读多少,没有数据则会一直等待.所以一般情况下:我们读取数据都需要采用 ...
- SOCKET网络编程细节问题1
SOCKET网络编程快速上手(二)——细节问题(1) 三.细节问题一个也不能少 Socket编程说简单也简单,程序很容易就能跑起来,说麻烦还真是麻烦,程序动不动就出问题.记得刚开始写网络代码的时候,那 ...
- Linux Socket 网络编程
Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...
- Python Socket 网络编程
Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...
- Python全栈【Socket网络编程】
Python全栈[socket网络编程] 本章内容: Socket 基于TCP的套接字 基于UDP的套接字 TCP粘包 SocketServer 模块(ThreadingTCPServer源码剖析) ...
- python之Socket网络编程
什么是网络? 网络是由节点和连线构成,表示诸多对象及其相互联系.在数学上,网络是一种图,一般认为专指加权图.网络除了数学定义外,还有具体的物理含义,即网络是从某种相同类型的实际问题中抽象出来的模型.在 ...
- Python之路【第七篇】python基础 之socket网络编程
本篇文章大部分借鉴 http://www.cnblogs.com/nulige/p/6235531.html python socket 网络编程 一.服务端和客户端 BS架构 (腾讯通软件:ser ...
随机推荐
- 洛谷 - P1462 - 通往奥格瑞玛的道路 - 二分 - Dijkstra
https://www.luogu.org/problem/P1462 感觉,要二分最大收费权的城市,把小于等于它的全部插进去,Dijkstra一下求出最小的血量.这样感觉太暴力了. 考虑只有1000 ...
- letsencrypt 免费SSL证书申请, 自动更新
Let's Encrypt 泛域名 证书申请 及自动更新 关键字:SSL证书.HTTPS 初次申请 1. 下载certbot wget https://dl.eff.org/certbot-auto ...
- 发布一本用 GitBook 编辑的书
在上一篇的文章里,我们已经写好了一本名叫 erdong-first-book 的书,但是在本地浏览很不方便,我们希望放到网络上,可以随时.方便的访问这个书籍.这个需求可以使用多种方式来实现,比如第一种 ...
- Chrome浏览器记住密码后input框黄色背景且背景图片不显示的问题
Chrome浏览器记住密码后再进入登录页后,输入框背景颜色变为黄色,字体颜色变成#000黑色,且添加的背景图片也那不显示了,进入审查元素后,发现浏览器默认给输入框添加了样式,并且无法通过importa ...
- styled-components缺点
缺点 不能用 stylelint 检查你的 Css 代码 在使用 styled-components 的过程中也会遇到一些问题,比如我们的项目会用stylelint来做样式代码的检查,但是使用了 st ...
- 前端开发HTML&css入门——CSS&选择器练习
CSS 层叠样式表 (Cascading Style Sheets)css可以用来为网页创建样式表,通过样式表可以对网页进行装饰.所谓层叠,可以将整个网页想象成是一层一层的结构,层次高的将会覆盖层次低 ...
- 用eclipse怎么打war包?
用eclipse怎么打war包? 在服务器上部署很多都是用war包进行部署的,eclipse是很友好的支持把java项目打成war包的,下面就把打war的经验写出来,供大家参考 百度经验:jingya ...
- java创建对象的5种方法
java是面向对象的,所以在使用中经常会去创建对象,而我们一般创建对象只会使用new关键字去创建,这里给大家总结一下在java中创建对象的5中方法: 使用new关键字 } → 调用了构造函数 使用Cl ...
- MySQL参数化有效防止SQL注入
sql语句的参数化,可以有效防止sql注入 注意:此处不同于python的字符串格式化,全部使用%s占位 from pymysql import * def main(): find_name = i ...
- Centos安装PHP PS:LAMP环境时,为少出错误,先安装一下编译环境
下面安装PHP时数显很多问题,为了减少问题,直接安装先yum一下 yum -y install gcc pcre pcre-devel gcc-c++ autoconf libxml2 libxml2 ...