Socket 简介
1. 实验介绍
1.1 实验内容
本节实验主要是对前一节 TCP/IP 协议的补充,主要讲解了在网络通信过程中常用到的 Socket 套接字,通过对套接字的介绍能够对网络通信有更全面的理解。本节实验主要涉及到了 Socket 的函数、Socket 的通信和 Socket 的应用。
1.2 实验知识点
- Socket 介绍
- Socket 函数
- Socket 通信
- Socket 应用
2. Socket 介绍
2.1 Socket 概述
套接字(socket)是支持 TCP/IP 协议的网络通信的基本操作单元。也是网络通信过程中端点的抽象表示,包含网络通信必须的五种信息:连接使用的协议,本地主机的 IP 地址,本地进程的协议端口,远地主机的 IP 地址,远地进程的协议端口。
而我们知道在本地进行进程间通信可以通过 PID(progress ID )来进行唯一标识,但是若继续沿用 PID 来标识网络中的通信就会发生很大几率的冲突。因此, Socket 套接字就是用来对网络中的进程进行唯一的标识,主要格式: IP 地址 + 协议 + 端口号
2.2 Socket 抽象层的体系结构
前一节实验我们知道了 TCP/IP 协议族包括:应用层、网络层、传输层和网络接口层,而 Socket 所在位置如图所示,Socket 是应用层与 TCP/IP 协议族通信的中间的一个软件抽象层。
3. Socket 函数
关于套接字我们常会在编程中使用到的三个函数,分别为 socket(),bind(),listen()
3.1 socket() 函数
创建 socket 需要指定三个参数分别为 domain,type,protocol。
domain:协议域也称为协议族,决定了 socket 的地址类型,在通信中必须采用对应的地址。常用的协议族包括:
- AF_INET IPv4 协议
- AF_INET6 IPv6 协议
type:指定 socket 类型。常见的类型包括:
- SOCK_STREAM 字节流套接字
- SOCK_DGRAM 数据报套接字
- SOCK_RAW 原始套接字
protocol:指定协议。常用的协议包括:
- IPPROTO_TCP TCP 传输协议
- IPPTOTO_UDP UDP 传输协议
3.2 bind() 函数
bind() 函数主要是把地址族中的特定地址赋给 socket。
参数说明:
- sockfd :socket 描述字,是通过 socket() 函数创建的唯一标识的一个 socket。
- addr:是一个 const struct sockaddr 指针,指向需要绑定给 sockfd 的协议地址。地址结构也会根据地址创建 socket 的地址协议族的不同而不同。
- addrlen:地址长度 。
3.3 listen() 函数
主要用来监听 socket,如果客户端这时调用 connect() 发出连接请求,服务器端就会接收到这个请求。
参数说明:
- sockfd:需要监听的 socket 描述字
- backlog:相应的 socket 可以排队的最大连接个数。
注:socket() 函数创建的 socket 默认是一个主动类型,listen() 函数将 socket 变为被动类型的,来等待客户的连接请求。
3.4 connect() 函数
调用 connect() 函数主要是用来发出连接请求,然后服务器端就接收到这个请求。其中,客户端通过调用 connect() 函数来建立与 TCP 服务器的连接。
参数说明:
- sockfd:客户端的 socket 描述字
- addr:服务器的 socket 地址
- addrlen:socket 地址的长度
3.5 accept() 函数
TCP 服务器端依次调用 socket()、bind()、listen() 后,就会监听指定的 socket 地址。而 TCP 客户端依次调用 socket()、connect() 后就向 TCP 服务器发送了一个连接请求。TCP 服务器监听到这个请求之后,就会调用 accept() 函数取接收请求,这样连接就建立好了。
参数说明:
- sockfd:监听套接字,主要用来监听一个端口。
- addr:一个结果参数,用来接受返回值,返回值指定客户端的地址,如果不感兴趣可以将返回值设置为 NULL。
- addrlen:主要用来标识 addr 结构的大小,即所占的字节个数。
如果 accept 返回成功,那么客户端和服务器建立连接,服务器通过返回的 socket 返回字来完成后续和客户端的通信。
3.6 read()/write() 函数
在客户端和服务器建立连接后,通过调用 read()/write() 函数进行网络 I/O 的读写操作,完成进程间的通信。
在多数情况下,推荐使用的读写函数是 recvmsg()/sendmsg() 函数。
3.7 close() 函数
在完成读写操作后就需要关闭相应的 socket 描述字,因此这里就用到了 close()函数。
当 socket 标记为已关闭 ,只是改变相应 socket 描述字的引用计数 ,当引用计数变为 0 的时候,触发 TCP 客户端向服务器发送终止连接请求。
4. 服务器/客户端交互流程
在实现网络间的通信交流,以及数据交换这一过程中,socket 将进行通信连接的两端划分为服务器端和客户端,服务器端在一个端口上进行监听请求,一旦有客户端的访问请求,服务器端就会和客户端进行通信,交换数据。
可以通过下面这个流程来直观了解一下这个过程:
5. Socket 应用
5.1 Socket 编程应用
在实现应用程序访问下层网络服务是,就会用到 Socket 这个端口来完成不同主机间的通信,从而实现数据交换。
我们通过两个简单的 python 小程序,来实现客户机和服务器间的通信。(后面会有章节详细介绍 python 编程,本实验可以先初步感受一下)
通过 vim 编辑器分别编写服务器端和客户端的 python 程序。
服务器端:
- Server.py
# coding=utf-8
import socket # 加载 socket 模块
HOST = '127.0.0.1' # 设置 HOST IP 地址
PORT = 1234 # 设置端口号
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 定义 socket 类型,网络通信,TCP
s.bind((HOST, PORT)) # socket 绑定的 IP 与端口
s.listen(1) # 开始TCP监听
while 1:
conn, addr = s.accept() # 接受 TCP 连接,并返回新的 socket 与 IP 地址
print'Connected by', addr # 输出客户端的IP地址
while 1:
data = conn.recv(100) # 把接收的数据实例化
print(data)
conn.sendall(data)
conn.close() # 关闭连接
客户端:
- Client.py
# coding=utf-8
import socket # 加载 socket 模块
import time # 加载系统时间模块
HOST = '127.0.0.1'
PORT = 1234
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 定义 socket 类型,网络通信,TCP
s.connect((HOST, PORT)) # 要连接的 IP 与端口
while 1:
s.sendall('this is python socket') # 把命令发送给对方端口
data = s.recv(1024) # 把接收的数据定义为变量
print (data) # 输出变量
time.sleep(3) #设置休眠时间
s.close() # 关闭连接
编写好之后,打开两个终端分别运行 Server.py 和 Client.py 程序。
$ python Server.py
$ python Client.py
运行成功后可以在终端看到服务器端和客户端每隔 3 秒(设置的休眠时间)就会进行一次通信,输出信息。
Socket 编程示例操作演示视频:
6. 总结
通过本节实验大家可以对 socket 套接字有一个全面的认识,同时初步了解了它在网络通信中的作用和重要性,以及通过一个简单的 Python 例子实践 socket 编程。