《TCP Sockets 编程》书摘
这周在读一本关于关于 TCP Sockets 编程的书,叫做 Working with TCP Sockets
。书中主要是以一个 Ruby 程序员的角度从较高层来讲 TCP Sockets 编程。对于我这个非计算机专业出身的程序员来说,读起来非常浅显易懂,学到了很多以前不知道的知识,这里主要纪录一下基本的知识。
Berkeley 套接字
Berkeley 套接字 API 是一种编程 API
,运作在实际的协议实现之上。它关注的是连接两个端点共享数据,而非处理分组和序列号。Berkeley 套接字 API 之所以能屹立不倒的一个关键原因就是: 你可以在无需了解底层协议的情况下使用套接字。Ruby 提供了套接字 API 的包装类,让我们可以更轻易的使用这一套 API。
服务器生命周期
服务器套接字用于侦听连接而非发起连接,其典型的生命周期如下:
- 创建
- 绑定
- 侦听
- 接受
- 关闭
下面是一段 Ruby 的示意代码:
require 'socket'
# 创建一个新的套接字
socket = Socket.new :INET, :STREAM
# 创建一个 c 结构体用来保存用于侦听的地址
addr = Socket.pack_sockaddr_in 4481, '0.0.0.0'
# 执行绑定
socket.bind addr
客户端生命周期
客户端的生命周期要比服务器短一些:
- 创建
- 绑定
- 连接
- 关闭
require 'socket'
socket = Socket.new :INET, :STREAM
# 发起到 google.com 端口 80 的连接 (梯子自备:))
remote_addr = Socket.pack_sockaddr_in 80, 'google.com'
socket.connect remote_addr
交换数据
可以将 TCP 连接想像成一串连接了本地套接字和远程套接字的管子
,我们可以沿着这跟管子发送、接受数据。
前面的代码中,我们创建套接字的时候传入了一个 :STREAM
的选项,该选项表明我们希望使用一个 流套接字
。TCP 是一个基于流的协议。如果我们在创建套接字的时候没有传入 :STREAM 选项,那就无法创建 TCP 套接字。
从应用程序的代码的角度上来说,TCP 连接提供了一个不间断的、有序的通信流。只有流,别无其他
。
data = %w(a b c)
data.each do |piece|
write_to_connection(piece)
end
# 下面的代码在一次操作中读取全部数据
result = read_from_connection #=> ['a', 'b', 'c']
上面代码表明,流并没有消息边界
的概念。即便是客户端分别发送了3份数据,服务器在读取时,也是将其作为一份数据来接受。它并不知道客户端时分批发送的数据。要注意的时,极端并不保留消息边界,但是流中内容的次序还是会保留的。