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 编程。

results matching ""

    No results matching ""