Python学习23——Socket套接字编程
文章目录
什么是Socket
socket是应用层 与 传输层 中间的软件抽象层,是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面.
对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Socket套接字的发展史
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。
因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。
一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯,这也被称进程间通讯或 IPC。
套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
-
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
-
基于网络类型的套接字家族
套接字家族的名字:AF_INET
还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET。
TCP与UDP
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)
对比 | TCP | UDP |
---|---|---|
是否连接 | 面向连接 | 无连接 |
是否可靠 | 可靠传输,使用流量控制和拥塞控制 | 不可靠传输,不使用流量控制和拥塞控制 |
连接对象个数 | 只能是一对一通信 | 支持一对一,一对多,多对多交互通信 |
传输方式 | 面向字节流 | 面向报文 |
首部开销 | 首部最小20字节,最大60字节 | 首部开销小,仅8字节 |
适用场景 | 适用于要求可靠传输的应用,例如文件传输 | 适用于实时应用(IP电话、视频会议、直播等) |
基于TCP协议通信的套接字程序
服务端要满足的特性:
1.一直对外提供服务
2.并发地提供服务
基础版:
server.py
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、插手机卡
phone.bind(("127.0.0.1", 8080)) # 本地回环
# 3、开机
phone.listen(5)
print('starting %s:%s' %("127.0.0.1", 8080))
# 4、等电话链接
conn, client_addr = phone.accept()
# 5、收/发消息
data = conn.recv(1024) # 最大接收的字节个数
print("收到的客户端数据:", data.decode('utf-8'))
conn.send(data.upper())
# 6、关闭
conn.close() # 挂电话
phone.close() # 关机
client.py
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、拨电话
phone.connect(("127.0.0.1", 8080))
# 3、发/收消息
phone.send("hello".encode('utf-8'))
data = phone.recv(1024)
print("服务的返回的数据:", data.decode('utf-8'))
# 4、关闭
phone.close()
加入循环:
server.py
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、插手机卡
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(("192.168.11.97", 8080)) # 本地回环
# 3、开机
phone.listen(5)
print('starting %s:%s' %("192.168.11.97", 8080))
# 4、等电话链接=>链接循环
while True:
conn, client_addr = phone.accept()
print(client_addr)
# 5、收/发消息=>通信循环
while True:
try:
data = conn.recv(1024) # 最大接收的字节个数
if len(data) == 0: # 针对linux系统
break
print("收到的客户端数据:", data.decode('utf-8'))
conn.send(data.upper())
except Exception: # 针对windows系统
break
# 6、关闭
conn.close() # 挂电话
phone.close() # 关机
client.py
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、拨电话
phone.connect(("192.168.11.97", 8080))
# 3、发/收消息=>通信循环
while True:
msg = input(">>: ").strip()
phone.send(msg.encode('utf-8'))
data = phone.recv(1024)
print("服务的返回的数据:", data.decode('utf-8'))
# 4、关闭
phone.close()
远程执行命令:服务端开启后,客户端可以直接输入命令控制服务端,服务端返回输出的值
server.py
import socket
import subprocess
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、插手机卡
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加
phone.bind(("127.0.0.1", 8080)) # 本地回环
# 3、开机
phone.listen(5)
print('starting %s:%s' % ("127.0.0.1", 8080))
# 4、等电话链接=>链接循环
while True:
conn, client_addr = phone.accept()
print(client_addr)
# 5、收/发消息=>通信循环
while True:
try:
cmd = conn.recv(1024) # 最大接收的字节个数
if len(cmd) == 0: # 针对linux系统
break
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
res=obj.stdout.read()+obj.stderr.read() # ???
print(res)
conn.send(res)
except Exception: # 针对windows系统
break
# 6、关闭
conn.close() # 挂电话
phone.close() # 关机
client.py
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、拨电话
phone.connect(("127.0.0.1", 8080))
# 3、发/收消息=>通信循环
while True:
cmd = input("[root@localhost]# ").strip()
phone.send(cmd.encode('utf-8'))
data = phone.recv(1024)
print(data.decode('gbk'))
# 4、关闭
phone.close()
基于udp协议的套接字通信
# TCP VS UDP协议
# 1、可靠性
# tcp协议是可靠协议:
# 对方必须回复一个ack确认信息,才会将自己这端的数据从内存中删除
# udp协议不可靠:
# 发送一条消息就会立即删除,不管对方是否接收到
# 2、有无链接
# tcp有链接,udp无链接
# 3、传输数据的效率
# udp更高
# 4、粘包问题
# udp协议称之为数据报协议,每次发送都是一个完整的数据报,一个发送唯一对应一个接收
# 所以udp协议没有粘包问题
server.py
from socket import *
import time
server = socket(AF_INET, SOCK_DGRAM)
server.bind(('127.0.0.1', 8080))
while True:
data, client_addr = server.recvfrom(1024)
time.sleep(10)
server.sendto(data.upper(), client_addr)
client.py
from socket import *
client = socket(AF_INET, SOCK_DGRAM)
while True:
msg = input('>>: ').strip()
client.sendto(msg.encode("utf-8"), ('127.0.0.1', 8080))
data, server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))
基于socketserver模块实现并发tcp
server.py
# from socket import *
#
#
# server=socket(AF_INET,SOCK_STREAM)
# server.bind(('127.0.0.1',8080))
# server.listen(5)
#
# while True:
# conn,addr=server.accept()
# print(addr)
#
# while True:
# try:
# data=conn.recv(1024)
# if len(data) == 0:break
# conn.send(data.upper())
# except Exception:
# break
#
# conn.close()
# 支持并发版本
import socketserver
class MyRequestHandler(socketserver.BaseRequestHandler):
def handle(self): # 处理通信
print(self.client_address)
while True:
try:
data = self.request.recv(1024) # self.request=>conn
if len(data) == 0: break
self.request.send(data.upper())
except Exception:
break
self.request.close()
if __name__ == '__main__':
s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyRequestHandler, bind_and_activate=True)
s.serve_forever()
client.py
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input(">>: ").strip()
if len(msg) == 0:
continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
基于socketserver模块实现并发udp
server.py
import socketserver
class MyRequestHandler(socketserver.BaseRequestHandler):
def handle(self): # 处理通信
data,server=self.request
server.sendto(data.upper(),self.client_address)
if __name__ == '__main__':
s = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyRequestHandler, bind_and_activate=True)
s.serve_forever()
client.py
from socket import *
client = socket(AF_INET, SOCK_DGRAM)
while True:
msg = input('>>: ').strip()
client.sendto(msg.encode("utf-8"), ('127.0.0.1', 8080))
data, server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))
阿里云部署
server.py
# coding:utf-8
# from socket import *
#
#
# server=socket(AF_INET,SOCK_STREAM)
# server.bind(('127.0.0.1',8080))
# server.listen(5)
#
# while True:
# conn,addr=server.accept()
# print(addr)
#
# while True:
# try:
# data=conn.recv(1024)
# if len(data) == 0:break
# conn.send(data.upper())
# except Exception:
# break
#
# conn.close()
# 支持并发版本
import socketserver
class MyRequestHandler(socketserver.BaseRequestHandler):
def handle(self): # 处理通信
print(self.client_address)
while True:
try:
data = self.request.recv(1024) # self.request=>conn
if len(data) == 0: break
self.request.send(data.upper())
except Exception:
break
self.request.close()
if __name__ == '__main__':
s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyRequestHandler, bind_and_activate=True)
s.serve_forever()
client.py
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('121.199.45.113',8080))
while True:
msg=input(">>: ").strip()
if len(msg) == 0:
continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))