PythonSocket(套接字)

Socket(套接字)

客户端/服务器架构(C/S架构)

概念

主从式架构 (也称客户端/服务器架构、C/S架构),是一种网络架构,它把客户端(Client)与服务器(Server)区分开。每一个客户端软件的实例都可以向一个服务器或应用程序服务器发出请求。有很多不同类型的服务器,例如文件服务器、游戏服务器等。

Socket简介

概念

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。为了简化计算机程序与TCP/IP协议族的复杂交互协议,封装了更方便的接口以便更快速的让用户组织数据,进行网络传输。
更形象的来说,套接字实际上是一个通信端点,每个套接字都有一个套接字序号,包括主机的IP地址与一个16位的主机端口号,即:主机IP地址:端口号。

Socket种类

为满足不同情况下的通信需求,通常网络会提供三种Socket以供选择。

  • 流式套接字(SOCK-STREAM)
    是一种可靠的、面向连接的双向数据传输服务,实现了数据无差错、无重复的发送。
    通常使用TCP协议来实现字节流的传输,以满足需要发送大量的数据或者对数据传输有较高的要求时,可以使用流式套接字。

  • 数据报套接字(SOCK-DGRAM)
    是一种无连接、不可靠的双向数据传输服务。
    数据在传输过程中可能会丢失或重复,并且不能保证在接收端按发送顺序接收数据。
    通常使用UDP协议来实现数据报套接字。在出现差错的可能性较小或允许部分传输出错的应用场合,可以使用数据报套接字进行数据传输,这样通信的效率较高。

  • 原始套接字(SOCK-RAW)
    该套接字允许对较低层协议(如IP或ICMP)进行直接访问,常用于网络协议分析,检验新的网络协议实现,也可用于测试新配置或安装的网络设备。

伯克利套接字

  • 概念

伯克利套接字,又称为BSD 套接字(BSD sockets)是一种应用程序接口(API),用于网络套接字与Unix域套接字,包括了一个用C语言写成的应用程序开发库,主要用于实现进程间通讯,在计算机网络通讯方面被广泛使用。

伯克利套接字应用程序接口形成了事实上的网络套接字的标准精髓。 大多数其他的编程语言使用与这套用C语言写成的应用程序接口类似的接口。 这套应用程序接口也被用于Unix域套接字(Unix domain sockets),后者可以在单机上为进程间通讯(IPC)的接口。

  • 使用伯克利套接字的系统
    • Windows Sockets (Winsock) ,和Berkeley Sockets很相似,最初是为了便于移植Unix程序。
    • Java Sockets
    • Python sockets
    • Perl sockets

Python语言下套接字(Socket)简单使用示例

基于TCP协议的Socket

可靠的、面向连接的双向数据传输。

  • 单服务端、单客户端示例
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
32
33
# ---------- 服务端 ----------
import socket

sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 此处作用解决Address already in use的情况,可以加入socket配置重用ip和端口
sk.bind(('127.0.0.1', 8088))
sk.listen()
conn, addr = sk.accept()
while True:
res = conn.recv(1024).decode('utf-8')
if res == 'bye':
break
else:
print(res)
info = input('请输入:\n>>>').encode('utf-8')
conn.send(info)
conn.close()
sk.close()

# ---------- 客户端 ----------
import socket

sk= socket.socket()
sk.connect(('127.0.0.1', 8088))
while True:
info = input('请输入:\n>>>').encode('utf-8')
sk.send(info)
res = sk.recv(1024).decode('utf-8')
if res == 'bye':
break
else:
print(res)
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
28
29
30
31
32
33
# ---------- 服务端 ----------
import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 8088))
# listen([backlog])中的[backlog]参数代表服务端允许多少客户端连接到服务端,即阻塞队列长度,所以一共能与服务器连接的客户端共有backlog+1个
sk.listen(3)
while True:
conn, addr = sk.accept() # sk.accept()放在循环内部是为了每次与同一个客户端交互结束后会重新建立conn连接,以便下一个客户端连入
res = conn.recv(1024).decode('utf-8')
if res == 'bye':
break
else:
print('收到',res)
info = input('请输入>>>').encode('utf-8')
conn.send(info)
conn.close() # 同上面sk.accept()的作用
sk.close()

# ---------- 客户端 ----------
import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 8088))
while True:
info = input('请输入>>>').encode('utf-8')
if info == 'bye':
break
else:
sk.send(info)
res = sk.recv(1024).decode('utf-8')
print(res)
sk.close()

基于UDP协议的Socket

无连接、不可靠的双向数据传输

  • 单服务端、单客户端示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ---------- 服务端 ----------
import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udp_server_client.bind(ip_port)
while True:
msg,addr=udp_server_client.recvfrom(BUFSIZE)
print(msg,addr)
udp_server_client.sendto(msg.upper(),addr)

# ---------- 客户端 ----------
import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
msg=input('>>: ').strip()
if not msg:continue
udp_server_client.sendto(msg.encode('utf-8'),ip_port)
back_msg,addr=udp_server_client.recvfrom(BUFSIZE)
print(back_msg.decode('utf-8'),addr)
  • 单服务端、多客户端示例
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

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1', 8888))
while True:
msg, addr = sk.recvfrom(1024)
print('消息地址:' + addr)
if msg.decode('utf-8') == 'bye':
break
else:
print(msg.decode('utf-8'))
info = input('>>>').encode('utf-8')
sk.sendto(info, addr)
sk.close()

# ---------- 客户端 ----------
import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ('127.0.0.1', 8888)
while True:
info = input('client:')
info = ('\033[33m来自client的消息 :%s\033[0m' % info).encode('utf-8')
sk.sendto(info, ip_port)
msg, addr = sk.recvfrom(1024)
if msg.decode('utf-8') == 'bye':
break
else:
print(msg.decode('utf-8'))
sk.close()