python - socket网络编程
socket 是什么?
socket (又叫套接字)
socket 是应用层与TCP/IP 协议簇通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议簇封装在socket接口后面,对用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/ip协议,socket已经为我们封装好了,我么你只需要遵循socket的规定去编程,写出程序自然就遵循tcp/ip标准。
socket分类:
基于文件类型的
AF_UNIX
unix下一切皆文件,基于文件的socket调用的就是底层的文件系统来取数据,两个套接字进程运行在泳衣机器,乐意通过访问同一个文件系统间接完成通信。
基于网络的
AF_INET
还有AF_INET6,还有一些其他的地址家族。不过,他们要么是用于某平台,要么就是已经被废弃。AF_INET是使用最广泛的,python支持很多中地址家族,但是由于我们之关系年网络编程,所有大部分时间我们只用使用了AF_INET。
socket工作流程:
拓展知识:
1.tcp三次握手和四次挥手
2.SYN洪水攻击
基础示例:
server:(TCP)
import socket # 导入 socket 模块 s = socket.socket() # 创建 socket 对象 host = "127.0.0.1" # 获取本地主机名 port = 12345 # 设置端口 s.bind((host, port)) # 绑定端口 s.listen(5) # 等待客户端连接 while True: c, addr = s.accept() # 建立客户端连接。 print ('连接地址:', addr) info ="hello!" c.send(info.encode()) c2 = c.recv(1024) print(c2) c.close() # 关闭连接
s.close()
client:
#--------------- s = socket.socket() # 创建 socket 对象 host = "127.0.0.1" # 获取本地主机名 port = 12345 # 设置端口号 s.connect((host, port)) s2 = s.recv(1024).decode() info = "nihao!" s.send(info.encode()) print(s2,type(s2)) s.close()
进阶:
server端:(TCP方式)
import socket re = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #地址重用的解决办法 re.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) re.bind(("127.0.0.1",12345)) re.listen() while True: print("开始监听----") a, b = re.accept() print(b, "已接入链接....") try: while True: info = a.recv(1024) a.send(info) a.close() except ConnectionResetError as J: print("远程主机已断开......") re.close()
client端:
import socket con = socket.socket() con.connect(("127.0.0.1",12345)) while True : info = input(">>>") con.send(info.encode()) msc = con.recv(1024) print(msc.decode()) con.close()
server端:(utp方式)
import socket ss = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) ip_port = ("127.0.0.1",8080) ss.bind(ip_port) while True: #元组:(data,IP_port) data,addr = ss.recvfrom(1024) print(data) ss.sendto(data.upper(),addr)
client端:
import socket ip_port = ("127.0.0.1",8080) cli = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True: data = input(">>>:") cli.sendto(data.encode("UTF-8"),ip_port) data2,addr = cli.recvfrom(1024) print(data2)
TCP、UDP使用场景
TCP协议需要三次握手通信成功后进行建立连接.
场景:互联网和企业网上的客户端应用、数据传输的性能必须让位于数据传输的完整性、可控制性和可靠性时,TCP协议是当然的选择。
UDP协议是直接发送,不会判断是否接收和发送成功.
场景: 当强调传输性能而不是传输的完整性时,如:音频和多媒体应用,UDP是最好的选择。QQ使用的是UDP协议聊天,因为用户不需要等待就会接收。
粘包现象:
实例:(socket-TCP + subprocess)
server端:
import socket import subprocess ip_port = ("127.0.0.1",8080) buffer_size = 1024 ss = socket.socket(socket.AF_INET,socket.SOCK_STREAM) ss.bind(ip_port) ss.listen(5) while True: print("开始监听链接...") conn,addr = ss.accept() print(addr,"已链接....") while True: try: data = conn.recv(buffer_size) if data == "":continue data_de = data.decode('utf-8') data_msg = subprocess.Popen(data_de,shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE) if data_msg.stderr.read().__len__(): data_send = "输入错误,请重新输入..".encode('gbk') else: data_send = data_msg.stdout.read() conn.send(data_send) except ConnectionResetError : print("远程主机已断开....") break conn.close() ss.close()
client端:
import socket ip_add = ("127.0.0.1",9999) buff_size = 1024 se = socket.socket(socket.AF_INET,socket.SOCK_STREAM) se.connect(ip_add) while True: data = input(">>>:") if data == "":continue se.send(data.encode("utf-8")) info = se.recv(1024) print(info.decode("gbk")) se.close()
出现问题:
1.执行ipconfig 命令后 返回的数据并不完整.
2.执行第二条命令时,还是第一条命令的显示.
解决粘包方案:
server:
import socket import subprocess import json import struct #常量定义 ip = "127.0.0.1" port = 9999 ip_port = (ip, port) buffer_size = 1024 #socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(ip_port) server_socket.listen(5) while True: print("开始监听.....") conn, addr = server_socket.accept() print(addr, "已接入....") while True: try: #获取指令 zl_b = conn.recv(buffer_size) print(zl_b) zl_s = zl_b.decode() #指令执行返回数据 data = subprocess.Popen(zl_s, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) data_Y_b = data.stdout.read() data_E_b = data.stderr.read() #封装数据总长度 data_len = len(data_Y_b) + len(data_E_b) data_dict = {"data_dict": data_len} json_data_s = json.dumps(data_dict) #封装head部长度 json_data_b = json_data_s.encode() head_len_i = len(json_data_b) #封装前4个字节长度 head_len_b = struct.pack("i", head_len_i) #发送4个字节,发送head,发送数据 conn.send(head_len_b) print(len(head_len_b)) conn.send(json_data_b) print(len(json_data_b)) conn.send(data_Y_b) print(len(data_Y_b)) conn.send(data_E_b) print(len(data_E_b)) except Exception: break conn.close() server_socket.close()
client:
import socket import json import struct ip = "127.0.0.1" port = 9999 ip_port = (ip,port) buffer_size = 1024 client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client_socket.connect(ip_port) while True: #发送指令至服务端. zhiling_s = input(">>>:").strip() if not zhiling_s:continue zhiling_b = zhiling_s.encode() client_socket.send(zhiling_b) #层层封装,收取对应数据对应长度,解封装. #获取前四个字节长度 head_len_b = client_socket.recv(4) head_len_i = struct.unpack('i',head_len_b)[0] print(head_len_i) #获取head部长度 json_data_b = client_socket.recv(head_len_i) print(len(json_data_b)) #获取数据总长度 json_data_s = json_data_b.decode() data_dict = json.loads(json_data_s) data_len = data_dict["data_dict"] print(data_len) #循环收取总数据 data_len_recv = 0 data = b"" while data_len_recv < data_len: data += client_socket.recv(buffer_size) data_len_recv += len(data) print(data.decode("gbk")) client_socket.close()
问题遗留:
client 端收取数据 data = struct.unpack("i",cli.recv(4))[0] data2 = iter(partial(cli.recv,data),b"").__next__().decode("gbk")
能否用两条命令就解决收取数据以及粘包问题?以上两条可以收取,依旧有粘包,待解决...