1.最基础版本的socket编程:
import socket #socket服务端: phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 phone.bind(('127.0.0.1',8080)) #插入手机卡 phone.listen(5) #开机 等待连接的连接数(5) 半连接池 conn,addr=phone.accept() #接电话 conn连接的线路 addr 对方ip print('tcp的连接: ',conn) print('客户端的地址:',addr) data=conn.recv(1024) #接收的大小 1024 print('from client msg: %s' %data) conn.send(data.upper()) #发消息 conn.close() #挂电话 phone.close() #关手机
import socket timeout=2 #这里对整个socket层设置超时时间。后续文件中如果再使用到socket,不必再设置 socket.setdefaulttimeout(timeout) #socket 客户端 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',8080)) client.send('hello'.encode('utf-8')) data=client.recv(1024) print(data) client.close()
简单的循环实现-socketserver和socketclient一直进行数据交互
import socket #服务端 #创建socket对象,指定家族簇,指定协议TCP=SOCK_STREAM phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #设置断开连接时不出错 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #绑定ip和port,注意传入一个参数元组类型 phone.bind(('127.0.0.1',8080)) phone.listen(5) #开机 等待连接的连接数(5) 半连接池 while True: #链接循环 conn,addr=phone.accept() #conn是client连接成功创建的对象,之后的数据交互都是通过conn print('tcp的连接: ',conn) print('客户端的地址:',addr) while True: #通讯循环 try: data=conn.recv(1024) #接收的大小 1024 #不能收空,空默认为没有收到 if not data:break #解决客户端断开,一直打印空。 print('from client msg: %s' %data) conn.send(data.upper()) #发消息 将client传入的数据转化为大写发送回去 except Exception: # break conn.close() #挂电话 phone.close() #关手机
import socket #客户端 #创建一个socket对象 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #利用socket对象进行连接server client.connect(('127.0.0.1',8080)) while True: msg=input('>>:') if not msg:continue #如果输入的数据是空,返回 client.send(msg.encode('utf-8')) #进行发送数据,socket之间数据交互必须是bytes类型的 data=client.recv(1024) #进行接收数据 print(data) client.close()
利用socket,远程执行命令并将命令执行的结果返回
服务端:
#远程执行命令server端 import socket,subprocess,struct phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) phone.listen(5) #开机 等待连接的连接数(5) 半连接池 while True: #链接循环 print('等待连接....') conn,addr=phone.accept() #接电话 conn连接的线路 addr 对方ip print('tcp的连接: ',conn) print('客户端的地址:',addr) while True: #通讯循环 try: cmd=conn.recv(1024) #接收的大小 1024 #不能收空,空默认为没有收到 if not cmd:break #解决客户端断开,一直打印空。 print('from client msg: %s' %cmd) res=subprocess.Popen(cmd.decode('utf-8'), #通过subprocess获取命令的执行结果 shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err=res.stderr.read() #获取错误信息 if err: #如果错误信息存在 back_msg=err #返回信息为错误信息 else: back_msg=res.stdout.read() #否则返回信息为标准输出 except Exception: # break #解决服务端粘包问题 conn.send(struct.pack('i',len(back_msg))) #'i':指定struct 返回的为4个字节 conn.sendall(back_msg) #sendall 。循环将所有的数据全部发送完 conn.close() #挂电话 phone.close() #关手机
客户端
import socket,struct #创建一个socket对象 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #进行连接服务器 client.connect(('127.0.0.1',8080)) #进入循环开始和服务器进行交互 while True: cmd=input('>>:').strip() if not cmd:continue #如果输入的是空,返回 client.send(cmd.encode('utf-8')) data_head=client.recv(4) #接收服务端用struct封装以后的数据,定长的为 4个字节 data_size=struct.unpack('i',data_head)[0] #利用struct获取到数据包的头部,获取到源数据的大小 recv_size=0 #用来保存接收到的数据大小 recv_bytes=b'' #用来保存接收到的数据 while recv_size <data_size: #判断有没有收完数据 res=client.recv(1024) #接收数据 recv_bytes+=res #拼接bytes字符 recv_size+=len(res) #计算接收到的数据的大小 print(res.decode('gbk')) #打印接收到的数据 client.close()
解决粘包问题: 发送json数据
import socket,subprocess,struct,json phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) #插入手机卡 phone.listen(5) #开机 等待连接的连接数(5) 半连接池 while True: #链接循环 print('等待连接....') conn,addr=phone.accept() #接电话 conn连接的线路 addr 对方ip print('tcp的连接: ',conn) print('客户端的地址:',addr) while True: #通讯循环 try: cmd=conn.recv(1024) #接收的大小 1024 #不能收空,空默认为没有收到 if not cmd:break #解决客户端断开,一直打印空。 print('from client msg: %s' %cmd) res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err=res.stderr.read() if err: back_msg=err else: back_msg=res.stdout.read() except Exception: # break # conn.send(back_msg) #解决服务端粘包问题 #conn.send(struct.pack('i',json.dumps(len(back_msg)))) #第一阶段,制作报头 head_dict={ 'data_size':len(back_msg) } head_json=json.dumps(head_dict) head_bytes=head_json.encode('utf-8') #第二阶段:发送报头的长度 conn.send(struct.pack('i',len(head_bytes))) #第三阶段:发送报头 conn.send(head_bytes) #第四阶段:发送真实数据 conn.sendall(back_msg) conn.close() #挂电话 phone.close() #关手机
客户端:
import socket,struct,json client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: cmd=input('>>:').strip() if not cmd:continue client.send(cmd.encode('utf-8')) #收报头的长度 data_head=client.recv(4) # data_size=json.load(struct.unpack('i',data_head)[0]) head_size=struct.unpack('i',data_head)[0] #收报头(根据报头长度) head_json=client.recv(head_size) head_dict=json.loads(head_json.decode('utf-8')) #获取真实数据长度 data_size=head_dict['data_size'] recv_size=0 recv_bytes=b'' while recv_size <data_size: res=client.recv(1024) recv_bytes+=res recv_size+=len(res) print(res.decode('gbk')) client.close()
socketserver解决只能一个客户端连接到server上的问题
服务端:
import socketserver #BaseRequestHandler 处理通讯 class FtpServer(socketserver.BaseRequestHandler): def handle(self): #必须实现handle方法,负责与客户端通讯 print(self.request) print(self.client_address) while True: try: data=self.request.recv(1024) self.request.send(data.upper()) except Exception: break if __name__ == '__main__': #ThreadingTCPServer 处理链接 s=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer) s.serve_forever() #链接循环--有了
客户端
from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg=input('>>:') client.send(msg.encode('utf-8')) data=client.recv(1024) print(data)
异常处理
try: names=['alex','liuhao'] names[sasa] except IndexError as e: print(e) except Exception as e: print(e) exit() else: print('什么错都没有') finally: print('无论有没有错都执行') print('keep going')
自定义异常处理
class WupeiqiException(Exception): def __init__(self, msg): self.message = msg try: raise WupeiqiException('我的异常') except WupeiqiException as e: print (e)
作业:开发一个支持多用户在线的FTP程序
要求:
- 用户加密认证
- 允许同时多用户登录
- 每个用户有自己的家目录 ,且只能访问自己的家目录
- 对用户进行磁盘配额,每个用户的可用空间不同
- 允许用户在ftp server上随意切换目录
- 允许用户查看当前目录下文件
- 允许上传和下载文件,保证文件一致性
- 文件传输过程中显示进度条
- 附加功能:支持文件的断点续传
FTPManager
import json,os,sys,hashlib def run(): #user_database = os.path.dirname(__file__)+'/'+'users'+'/' user_database=os.path.dirname(os.path.abspath(sys.argv[0]))+'/'+'users'+'/' print(user_database) while True: md5=hashlib.md5() name=input('create your acount:') if len(name)==0:continue elif os.path.isfile(user_database+name): print('用户已经存在') continue passwd=input('create your passwd:') if len(passwd)==0:continue passwd=passwd.encode(encoding='utf-8') md5.update(passwd) quota_size=input('enter your quota_size[b]:') if len(quota_size)==0:continue user_dir = os.path.dirname(os.path.abspath(sys.argv[0]))+'/'+'user_file/'+name os.mkdir(user_dir) user_dict={ 'name':name, 'passwd':md5.hexdigest(), 'quota_size':int(quota_size), 'use_quota':0 } with open(user_database+name,'w',encoding='utf-8') as f: f.write(json.dumps(user_dict)) break if __name__ == '__main__': run()