scoket 网络编程技术

一、网络编程技术概述

1.软件开发架构

  • 应用类 — C/S 架构

    C/S :客户端与服务器端架构,客户端 泛指 应用程序 EXE ,程序需要先安装才能运行,对系统环境依赖较大。

  • Web 类 — B/S 架构

    B/S :浏览器端与服务器端架构,用户通过 HTTP 请求服务器端相关的资源。

2.网络通信要素

  • IP 地址 :互联网协议地址
  • 端口 :设备与外界通讯交流的出口
  • 传输协议

IP 地址 精确到 具体的一台电脑,而 端口 精确到 具体的程序

3.通信协议

通信协议:互联网的核心就是由一堆协议组成,协议就是标准。

七层网络协议模型

二、Socket 通信流程

1.Socket 概念

Socket :是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。

​ 在设计模式中,Sorcket 其实就是一个门面模式,它把复杂的 TPC/IP 协议族隐藏在 Socket 接口后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议。

1.websocket (应用层) :通过 HTTP 协议进行传输,

2.socket (传输层) :通过 TCP 协议进行传输

2.Socket 实现方式:TCP 协议与 UDP 协议

  • TCP 协议 :可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。

    主要用途:Web 浏览器、电子邮件、文件传输程序

  • UDP 协议 : 不可靠的、无连接的服务,传输效率高,一对一、多对多、一对多、多对一、面向报文,尽最大的努力服务,无拥塞控制。

    主要用途:域名系统、视频流、IP 语音。

  • TCPUDP 都是 传输层 的。

TCP 协议流程图:

3. Socket 通信流程

  1. 服务器根据地址类型、Socket 类型、协议 创建 Scoket

  2. 服务器为 socket 绑定 IP 地址和端口号

  3. 服务器 socket 监听端口号请求 ,随时准备 接收客户端发送来的连接,这个时候服务器端的 Socket 并没有打开;

  4. 客户端创建 Socket

  5. 客户端打开 socket ,根据服务器 IP 地址端口号 试图连接服务器 socket

  6. 服务器 Scoket 接收到客户端的 socket 请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这个时候 socket 进入阻塞状态。

    阻塞状态:即 accept() 方法一直等到客户端返回连接信息后才返回,开始接收下一个客户端连接请求。

  7. 客户端连接成功,**向服务器发送连接状态信息**。

  8. 服务器 accept 方法返回,连接成功;

  9. 客户端向 socket 写入信息(或服务端向 socket 写入信息);

  10. 服务器读取信息(客户端读取信息);

  11. 客户端关闭;

  12. 服务器关闭。

Socket 常用方法

1.Python 的 Socket 编程

  • 低级别的网络服务 支持基本的 Socket ,它提供了标准的 BSD Socket API ,可以访问底层操作系统 Socket 接口的全部方法。
  • 高级别的网络服务 模块 SocketServer , 它提供了服务器中心类,可以简化网络服务器的开发。

2.什么是 Socket 编程

socket :又称 ”套接字“ ,应用程序通常通过 ”套接字“ 向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。

使用 socket() 函数来 创建 套接字

1
socket.socket([family[,type[,proto]]])
  • family :套接字家族可以使 AF_UNIX 或者 AF_INET .
  • type :套接字类型可以根据是面向 连接 的还是 非连接 分为 SOCK_STREAMSOCK_DGRAM .
  • protocol :一般不填默认为 0

3. Socket 服务端

  • 使用 socket 模块socket 函数 来创建一个 socket 对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。
  • 可以通过调用 bind(hostname,port)函数来指定服务的 port (端口)。
  • 调用 socket 对象accept 方法。该方法等待客户端的连接,并返回 connection 对象,表示已连接到客户端。

4. Socket 客户端

  • socket.connect(hosname, port) 方法 打开一个 TCP 连接到主机为 hostname 端口为 port 的服务商。
  • 连接后就可以从服务端获取数据,记住,操作完成 后需要 关闭连接

5.案例一(单个连接)

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import socket

ip_port = ('127.0.0.1', 9999)

# 1.创建 socket 对象
sk = socket.socket()

# 2.绑定 ip port
sk.bind(ip_port)

# 3.开启监听
sk.listen()
print("---服务已经启动---")

# 4.阻塞 等待连接 返回:socket连接对象(套接字)和 客户端IP
conn, addr = sk.accept()
print("客户端的地址 >>> :", addr)

# 5.接收数据---客户端数据
# recv() 需要文件宽度作为参数,一般为 1024
# 使用 decode() 进行解码
receive_data = conn.recv(1024).decode("utf-8")
print("接收客户端的数据 >>> :", receive_data)

# 6.发送数据
send_data = input("请输入 >>> :")
# sendall() 要求是 byte 类型,使用encode() 进行字符转换
conn.sendall(send_data.encode("utf-8"))

# 7.关闭 socket
conn.close()

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import socket

# 1.创建 socket
sk = socket.socket()

# 2.连接到服务器
sk.connect(('127.0.0.1', 9999))
print("服务端连接成功")

# 3.发送数据
send_data = input("请输入 >>> :")
# sendall() 要求是 byte 类型,使用encode() 进行转码
sk.sendall(send_data.encode('utf-8'))

# 4.接收服务端数据
server_data = sk.recv(1024).decode("utf-8")
print("接收服务端的数据 >>> :", server_data)

# 5.关闭 socket
sk.close()

案例二(多个连接):

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import socketserver

# 1.需要继承一个类
class sqServer(socketserver.BaseRequestHandler):
def handle(self):
print("聊天服务器上线了")
while True:
try:
# 接收客户端数据
self.client_data = self.request.recv(1024)
print(self.client_data.decode("utf-8"))

# 发送数据
send_data = input("请输入 >>> :")
self.request.sendall(send_data.encode("utf-8"))
except ConnectionResetError as e:
print("一个客户端关闭了连接")
break
self.request.close()

# 2.创建服务
# ThreadingTCPServer 是使用多线程
# ForkingTCPServer 是使用多进程处理并发
server = socketserver.ThreadingTCPServer(("127.0.0.1", 9998), sqServer)

# 3.一直在线
server.serve_forever()

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import socket

# 1.创建 socket
sk = socket.socket()

# 2.连接到服务器
sk.connect(('127.0.0.1', 9998))
print("服务端连接成功")

# 3.发送数据
while True:
send_data = "老王:" + input("请输入 >>> :")
if send_data == '老王:' + 'exit':
break
else:
# sendall() 要求是 byte 类型,使用encode() 进行转码
sk.sendall(send_data.encode('utf-8'))

# 4.接收服务端数据
server_data = sk.recv(1024).decode("utf-8")
print("接收服务端的数据 >>> :", server_data)

# 5.关闭 socket
sk.close()