The First Encounter With Socket

网络基础 TCP/IP

在讨论 Socket 之前,我们得先追本溯源,看看 TCP/IP 是什么。

计算机与网络设备之间的通信,就如大国之间的外交一样,需要一种规则。这种规则就成称为协议(protocol)。计算机网络协议中存在各种各样的内容:从电缆规格到 IP 地址的选定方法、双方建立通信的顺序,以及 Web 页面显示需要处理的步骤,等等。

而与互联网相关联的协议集合总称为 TCP/IP。

TCP/IP 的分层

TCP/IP 按层次分为4层:应用层、传输层、网络层和数据链路层。

layer detail
应用层 应用层决定了向用户提供应用服务时通信的活动。TCP/IP协议族内预存了各类通用的应用服务。比如 FTP (File Transfer Protocol )、HTTP、和DNS(Domain Name System)。
传输层 传输层提供处于网络连接中的两台计算机之间的数据。在传输层有两个性质不同的协议:TCP(Transmission Control Protocol)和 UDP (User Data Protocol)
网络层 网络层用来处理在网络上流动的数据包。数据包是网络传输的最小单位。该层规定了到达对方计算机的传输路线,并把数据包传送给对方。
链路层 用来处理连接网络的硬件部分,包括控制操作系统、硬件的设备驱动,NIC(Network Interface Card,网络适配器,即网卡),及光纤等物理可见部分。

TCP/IP 通信传输流

利用 TCP/IP 协议族进行网络通信时,会通过分层顺序与对方进行通信,发送端从应用层往下走,接收端则从应用层往上走。

TCP/IP 通信传输流

Socket 的基本概念

Socket 是对 TCP/IP 协议族的一种封装,是应用层与TCP/IP协议族通信的中间软件抽象层。它把复杂的 TCP/IP 协议族隐藏在 Socket API 后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议。也就是说, TCP/IP 提供给程序员做网络开发所用的接口就是 Socket 编程接口。

利用 Socket 建立网络连接的步骤与基本操作

利用 Socket 建立网络至少需要一对 Socket,其中一个运行于客户端,称为 ClientSocket ,运行于服务端的称为 ServerSocket。

套接字(Socket 的译名)之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
  1、服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
  2、客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。
  为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
  3、连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。

Socket 的基本操作(图是盗的)

常用的三种 Socket 类型

  • 流式 Socket(SOCK_STREAM):流式是一种面向连接的 Socket,针对于面向连接的 TCP服务应用
  • 数据报式 Socket(SOCK_DGRAM):数据报式 Socket 是一种无连接的 Socket,对应于无连接的UDP 服务应用。
  • Unix 域 Socket (Unix Domain Socket):Socket API原本是为网络通讯设计的,但后来在Socket 的框架上发展出一种IPC机制,就是Unix Domain Socket,用于同一台主机的进程间通讯。虽然网络socket也可用于同一台主机的进程间通讯(通过 loopback 地址127.0.0.1),但是Unix Domain Socket更有效率,不需要经过网络协议栈,只是将应用层数据从一个进程拷贝到另一个进程。

Socket 实践——在 Nodejs 中使用 TCP 套接字编程

服务端代码:

// server.js
const net = require("net");
const host = "127.0.0.1";
const port = 3001;

const server = net
  .createServer(c => {
    console.log(`客户端${c.remoteAddress}:${c.remotePort}已连接`);
     // "data" 事件处理函数
    c.on("data", data => {
      console.log(`来自客户端${c.remoteAddress}:${c.remotePort}的数据:${data}`);
      c.write(`已收到你的数据`);
    });
    c.on("end", () => {
      console.log(`客户端${c.remoteAddress}:${c.remotePort}断开连接`);
    });
  })
  .listen(port, host);

客户端代码:

// client.js
const net = require("net");
const host = "127.0.0.1";
const port = 3001;

const client = new net.Socket();

client.connect(port, host, () => {
  console.log(`连接至${host}:${port}`);
  // 向服务端发送数据
  client.write(`Hello!World!`);

  // "data" 事件处理函数
  client.on("data", data => {
    console.log(`服务器发回: ${data}`);
    client.destroy();
  });
});

client.on("close", function() {
  console.log(`断开连接`);
});


参考

Show Comments