Socket:套接字。作用:我们只需要安照socket的规定去编程,就不需要深入理解tcp/udp协议也可以实现
1:TCP协议
1.1 客户端服务端循环收发消息
# 1:引入stock模块(导包) import socket #2:创建服务端对象 tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #3:绑定接口地址和端口号 ip_port=("127.0.0.1",8000) back_log=5 buffer_size=1024 tcp_server.bind(ip_port) #4: 开启监听(设置监听数量) tcp_server.listen(back_log) print("服务端开始运行了") # 5:准备接收消息 conn, add = tcp_server.accept() while True: #6:接收消息 data=conn.recv(buffer_size) print("客户端发送的消息是:",data.decode("utf-8")) #7:发送消息 conn.send(data.upper()) # 8:关闭连接 conn.close() # 9:关闭服务端对象 tcp_server.close()
#1:引入模块 from socket import * #2:创建对象 tcp_client = socket(AF_INET,SOCK_STREAM) #3:建立连接 ip_port=("127.0.0.1",8000) buffer_size=1024 tcp_client.connect(ip_port) while True: msg=input("请输入待发送的消息:") #4:发消息 tcp_client.send(msg.encode("utf-8")) # 5:收消息 data=tcp_client.recv(buffer_size) print("收到服务端传来的消息",data.decode("utf-8")) tcp_client.close()
1.2 服务端循环链接请求来收发消息
# 1:引入stock模块(导包) import socket #2:创建服务端对象 tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #3:绑定接口地址和端口号 ip_port=("127.0.0.1",8000) back_log=5 buffer_size=1024 tcp_server.bind(ip_port) #4: 开启监听(设置监听数量) tcp_server.listen(back_log) print("服务端开始运行了") while True: # 5:准备接收消息 conn, add = tcp_server.accept() while True: # 客户端断开连接的时候会报错 try: # 6:接收消息 data = conn.recv(buffer_size) # 据说Linux系统即使客户端断开连接也不会抛异常,这是需要手处理一下 if not data:break print("客户端发送的消息是:", data.decode("utf-8")) # 7:发送消息 conn.send(data.upper()) except Exception: break # 8:关闭连接 conn.close() # 9:关闭服务端对象 tcp_server.close() # 改进方向 # 1:把5和8放入到循环中,这样就可以实现多连接了 # 2:在内部连接中设置break方法
#1:引入模块 from socket import * #2:创建对象 tcp_client = socket(AF_INET,SOCK_STREAM) #3:建立连接 ip_port=("127.0.0.1",8000) buffer_size=1024 tcp_client.connect_ex(ip_port) while True: msg=input("请输入待发送的消息:") #如果不加这句,当输入换行时,服务器端会阻塞 if len(msg)==0: continue #4:发消息 tcp_client.send(msg.encode("utf-8")) # 5:收消息 data=tcp_client.recv(buffer_size) print("收到服务端传来的消息",data.decode("utf-8")) tcp_client.close()
1.3 从代码层面解决 “通常每个套接字地址(协议/网络地址/端口)只允许使用一次。”问题,在bind前加下一句代码
tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
2:UDP协议
2.1 客户端服务端循环收发消息
# 1:引入stock模块(导包) from socket import * from time import strftime #2:创建服务端对象 udp_server =socket(AF_INET,SOCK_DGRAM) #3:绑定接口地址和端口号 ip_port=("127.0.0.1",8000) back_log=5 buffer_size=1024 udp_server.bind(ip_port) print("服务端开始运行了") while True: while True: # 客户端断开连接的时候会报错 try: # 6:接收消息 data ,add= udp_server.recvfrom(buffer_size) print("客户端发送的消息是:", data.decode("utf-8")) # 7:发送消息 time_fmt='%Y-%m-%d %X' data=strftime(time_fmt) udp_server.sendto(data.encode("utf-8"),add) except Exception: break # 9:关闭服务端对象 udp_server.close() # udp与tcp的不同之处 # 1:不需要连接s.listen() 监听和 s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来 # 2:通过recvfrom()接收数据;sendto()发送数据 # 3:既然没有连接,也就无所谓谁先启动,或者客户端关闭后是否会引发异常等情况
#1:引入模块 from socket import * #2:创建对象 udp_client = socket(AF_INET,SOCK_DGRAM) #3:建立连接 ip_port=("127.0.0.1",8000) buffer_size=1024 udp_client.connect_ex(ip_port) while True: msg=input("按任意键获取时间:") # 4:发消息 udp_client.sendto(msg.encode("utf-8"), ip_port) # 5:收消息 data, add = udp_client.recvfrom(buffer_size) print("收到服务端传来的时间", data) tcp_client.close()
3:练习
3.1 远程操作
需要使用subprocess模块
from socket import * import subprocess ip_port = ("127.0.0.1", 8089) back_log = 5 buffer_size = 1024 tcp_server = socket(AF_INET, SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: conn, add = tcp_server.accept() print(add) while True: try: cmd = conn.recv(buffer_size); if not cmd: conn.close() break res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, ) err = res.stderr.read() if err: cmd_res = err else: cmd_res = res.stdout.read() # 发 conn.send(cmd_res) except Exception as e: print(e) conn.close() break conn.close() top_server.close()
from socket import * ip_port=("127.0.0.1", 8089) back_log = 5 buffer_size = 1024 tcp_client = socket(AF_INET, SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd = input(">>: ").strip() if not cmd: continue if cmd == "quit": break tcp_client.send(cmd.encode("utf-8")) cmd_res = tcp_client.recv(buffer_size) print(cmd_res.decode("gbk")) tcp_client.close()
会出现粘包现象:1:tcp-ip内部算法优化,会把短时间内几个小的send信息包在一起发送(tcp-ip的send本身没有界限)2:一次发送量大,一次接受不全
from socket import * import subprocess ip_port = ("127.0.0.1", 8089) back_log = 5 buffer_size = 1024 tcp_server = socket(AF_INET, SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: conn, add = tcp_server.accept() print(add) while True: try: cmd = conn.recv(buffer_size); if not cmd: conn.close() break res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, ) err = res.stderr.read() if err: cmd_res = err else: cmd_res = res.stdout.read() # 为了解决粘包的问题,可以在发送的时候先把长度发过去 length=str(len(cmd_res)).encode("utf-8") conn.send(length) # 如果直接这么发的话,会出现由于tcp优化出现的粘包情况。 client_s = conn.recv(buffer_size); print("===>",client_s) if client_s==b"ready": conn.send(cmd_res) except Exception as e: print(e) conn.close() break conn.close() top_server.close()
from socket import * ip_port=("127.0.0.1", 8089) back_log = 5 buffer_size = 1024 tcp_client = socket(AF_INET, SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd = input(">>: ").strip() if not cmd: continue if cmd == "quit": break tcp_client.send(cmd.encode("utf-8")) res_length=tcp_client.recv(buffer_size); #把获取到的长度转化成intleiixng cmd_res="" if res_length: tcp_client.send("ready".encode("utf-8")) res_length = int(res_length.decode("gbk")) print(res_length) # 循环遍历接收 while len(cmd_res)<res_length: cur_res=tcp_client.recv(buffer_size).decode("gbk") cmd_res = cmd_res+cur_res print(cur_res) tcp_client.close()
# 通过引入struct模块,编制一个报文长度,实现一次发送 import struct from socket import * import subprocess ip_port = ("127.0.0.1", 8089) back_log = 5 buffer_size = 1024 tcp_server = socket(AF_INET, SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: conn, add = tcp_server.accept() print(add) while True: try: cmd = conn.recv(buffer_size); if not cmd: conn.close() break res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, ) err = res.stderr.read() if err: cmd_res = err else: cmd_res = res.stdout.read() # 为了解决粘包的问题,可以在发送的时候先把长度发过去 length=struct.pack("i",len(cmd_res)) conn.send(length) conn.send(cmd_res) print(struct.unpack("i",length)[0]) except Exception as e: print(e) conn.close() break conn.close() top_server.close()
# 通过引入struct模块,编制一个报文长度,实现一次发送 from socket import * import struct ip_port=("127.0.0.1", 8089) back_log = 5 buffer_size = 1024 tcp_client = socket(AF_INET, SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd = input(">>: ").strip() if not cmd: continue if cmd == "quit": break tcp_client.send(cmd.encode("utf-8")) #把获取到的长度转化成intleiixng cmd_res=b'' res_length = tcp_client.recv(4) length=struct.unpack("i",res_length)[0] # 循环遍历接收 while len(cmd_res) < length: cur_res = tcp_client.recv(buffer_size) cmd_res = cmd_res + cur_res print(cmd_res.decode("gbk")) tcp_client.close()
# 通过引入struct模块,实现一次发送 # 通过socketServer实现并发 import struct from socket import * import subprocess import socketserver ip_port = ("127.0.0.1", 8089) back_log = 5 buffer_size = 1024 # 定义一个实例化的类,主要用于收发消息 class My_server(socketserver.BaseRequestHandler): def handle(self): try: while True: # 收消息 cmd = self.request.recv(buffer_size) if not cmd: break res = subprocess.Popen(cmd.decode("utf-8"), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE) err = res.stderr.read() if err: cmd_res = err else: cmd_res = res.stdout.read() # 为了解决粘包的问题,可以在发送的时候先把长度发过去 length = struct.pack("i", len(cmd_res)) self.request.send(length) self.request.sendall(cmd_res) except Exception as e: print(e) #通过线程创建对象 if __name__=="__main__": s=socketserver.ThreadingTCPServer(ip_port,My_server) s.serve_forever()
# 通过引入struct模块,编制一个报文长度,实现一次发送 from socket import * import struct ip_port=("127.0.0.1", 8089) back_log = 5 buffer_size = 1024 tcp_client = socket(AF_INET, SOCK_STREAM) tcp_client.connect(ip_port) while True: cmd = input(">>: ").strip() if not cmd: continue if cmd == "quit": break tcp_client.send(cmd.encode("utf-8")) #把获取到的长度转化成intleiixng cmd_res=b'' res_length = tcp_client.recv(4) length=struct.unpack("i",res_length)[0] # 循环遍历接收 while len(cmd_res) < length: cur_res = tcp_client.recv(buffer_size) cmd_res = cmd_res + cur_res print(cmd_res.decode("gbk")) tcp_client.close()
4:FTP文件上传与下载
4.1实现客户端与服务端的调通
首先,创建两个文件夹FTP_server和FTP_client用于存放服务端和客户端的文件
4.1.1 FTP_server
创建一个bin文件,里面包含启动文件ftp_server.py
4.1.1.1 bin文件
# 主要用于启动 import os,sys # 此时的路径是当前执行文件的路径,相当于当前的bin目录下寻找core中的main方法 PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(PATH) from core import main if __name__ == '__main__': main.ArgvHandler()
创建一个conf文件,里面包含系统的配置信息,settings.py
4.1.1.2 conf文件
IP="127.0.0.1" PORT=8089
创建一个core文件,里面包含系统的核心代码,main.py和server.py
4.1.1.3 core文件
import optparse #引入optparse模块:其作用是把用户输入的命令匹配成字典形式,参考示例1 import socketserver #引入socketserver模块:其作用是创建连接对象,启动ftp服务端 from conf import settings #引入settings模块:其作用是通过配置文件拿到IP地址和端口号 from core import server #引入server模块:其作用是把主要的逻辑放置到server中, # 注意:由于main方法不是启动方法,启动方法在bin中,所以需要通过引用的方式实现 class ArgvHandler(): #功能1:用户输入命令-启动ftp_server def __init__(self): self.op = optparse.OptionParser() options, args = self.op.parse_args() self.verify_args(options, args) def verify_args(self,options, args): #命令分发,通过反射来实现 cmd=args[0] if hasattr(self,cmd): fun=getattr(self,cmd) fun() def start(self): print(" the server is working...") #启动方法 s=socketserver.ThreadingTCPServer((settings.IP,settings.PORT),server.ServerHandler) s.serve_forever() ''' 示例1:---开始 self.op = optparse.OptionParser() self.op.add_option("-s", "--server", dest="Server") self.op.add_option("-p", "--port", dest="Port") options, args = self.op.parse_args() print(options) print(options.Server) print(args) 示例1在Terminal中输入命令 D:CodeFTPFTP_serverin> python ftp_server.py -s 127.0.0.1 -p 8089 abc 示例1运行结果 {'Server': '127.0.0.1', 'Port': '8089'} 127.0.0.1 ['abc'] 示例1--结束 '''
import socketserver class ServerHandler(socketserver.BaseRequestHandler): #主要用于服务端的处理方法 def handle(self): print("ok")
创建一个logger文件,主要用户记录日志
4.1.1.4 logger文件
4.1.2 FTP_client
里面先简单设置一个ftp_client.py的测试客户端
import socket sk=socket.socket() sk.connect(("127.0.0.1",8089))