首先说一下TCP/IP 协议,OS七层
应用模型:
socket 软件抽象层(不负责发送数据,真正发送数据的是socket后面的协议)
socket
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
socket和file的区别:
- file模块是针对某个指定文件进行【打开】【读写】【关闭】
- socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
举个打电话的例子如图:

import socket ip_port = ('127.0.0.1',9999) #买手机 s= socket.socket() #封装好了TCP协议 ,生成套接字 #买手机卡 s.bind(ip_port) #绑定ip+协议+端口:用来唯一标示一个进程,ip_port必须是元组形式 #开机 s.listen(5) #定义可以挂起链接数(最大128个连接,队列等待) while True: #等待电话 conn,addr = s.accept() #返回两个对象(conn=通信链路,addr是客户端ip+port)accetp(接受客户端请求过程是一个阻塞过程 while True: #(用来接收一个连接重复发消息) try: #(捕捉客户端异常关闭ctrl+c) #收消息 recv_data = conn.recv(1024) #接受1024字节 收消息,阻塞 print(str(recv_data,encoding='utf-8')) if len(recv_data) == 0:break #客户端如果退出,服务端将受到空消息,退出 #发消息 send_data = recv_data.upper() conn.send(send_data) except Exception: break #挂电话 conn.close()

import socket ip_port = ('127.0.0.1',9999) #买手机 s= socket.socket() #封装好TCP协议的 #拨号 s .connect(ip_port) while True: #发送消息 send_data = input(">>:").strip() if send_data == 'exit':break if len(send_data) == 0:continue s.send(bytes(send_data,encoding='utf8')) #2.0可以发字符串,3.0只能发字节 #收消息 recv_data = s.recv(1024) print(str(recv_data,encoding='utf-8')) #挂断 s.close()
解决粘包问题:
服务端:1、send 数据长度
4、recv 收到确认信息,开始下一步
客户端:2、recv 获取数据长度
3、send 发送确认信息
5、recv 循环接收
基于socket 实现类ssh远程执行命令

import socket import subprocess ip_port = ('127.0.0.1',9999) s= socket.socket() #封装好了TCP协议 ,生成套接字 s.bind(ip_port) #绑定ip+协议+端口:用来唯一标示一个进程,ip_port必须是元组形式 s.listen(5) #定义可以挂起链接数(最大128个连接,队列等待) while True: conn,addr = s.accept() #返回两个对象(conn=通信链路)accetp过程是一个阻塞过程 while True: try: recv_data = conn.recv(1024) #接受1024字节 print(str(recv_data,encoding='utf-8')) if len(recv_data) == 0:break p = subprocess.Popen(str(recv_data,encoding='utf-8'),shell=True,stdout=subprocess.PIPE) #执行系统命令,Windows平台命令的标准输出是gbk编码,需要转换 res = p.stdout.read() #获取标准输出 if len(res) == 0: #执行错误命令,标准输出为空 send_data = 'cmd error' else: #解码 win系统的是gbk编码 gpk------>str------->utf8 send_data = str(res,encoding='gbk') send_data1 = bytes(send_data,encoding='utf-8') ready_tag = 'Ready|%s' % len(send_data1) conn.send(bytes(ready_tag,encoding='utf-8')) #发送数据长度 feedback = conn.recv(1024) #start 接受确认信息 feedback = str(feedback,encoding='utf-8') if feedback.startswith("Start"): conn.send(bytes(send_data,encoding='utf-8')) #发送数据 #conn.send(bytes(send_data,encoding='utf-8')) #编码 except Exception: break conn.close()

import socket ip_port = ('127.0.0.1',9999) s= socket.socket() #封装好TCP协议的 s .connect(ip_port) #链接服务端,如果服务端已经有一个链接,那么挂起 while True: #基于connect建立的链接循环发消息 send_data = input(">>:").strip() if send_data == 'exit':break if len(send_data) == 0:continue s.send(bytes(send_data,encoding='utf8')) #2.0可以发字符串,3.0只能发字节 #解决粘包问题 ready_tag = s.recv(1024) #收取带数据长度的字节Ready|len ready_tag = str(ready_tag,encoding='utf-8') if ready_tag.startswith('Ready'): #Ready|9998 msg_size = int(ready_tag.split('|')[-1]) #获取待接收数据长度 start_tag = 'Start' s.send(bytes(start_tag,encoding='utf-8')) #基于已经收到的待接收数据长度,循环接收数据 recv_size = 0 recv_msg = b'' while recv_size < msg_size: recv_data = s.recv(1024) recv_msg += recv_data recv_size += len(recv_data) print('MSG SIZE %s RECE SIZE %s ' % (msg_size,recv_size)) print(str(recv_msg,encoding='utf-8')) s.close()
总结:
1、python3.5的socket只能收发字节(2.7可以收发字符串)
2、只客户端退出,服务器端不退出
3、accept() 和recv()是阻塞的,阻塞的前提是基于链接正常(socket 除了accpet会阻塞,recv也会阻塞,如果发的是空就会阻塞)
4、listen(n)n代表:能刮起的链接数,如果n=1,代表可以链接一个,挂起一个,第三个拒绝。
5、服务端出现端口冲突:修改监听端口号
6、import SocketServer #2.0版本
import socketserver #3.0版本
socket 多并发

import socketserver import subprocess class MyServer(socketserver.BaseRequestHandler): #必须继承此父类 #方法名字必须是handle(父类里有handle方法但是是空,如果子类不写handle方法将会执行父类的handle) def handle(self): #conn = self.request self.request.sendall(bytes('欢迎致电10086,请输入0转人工服务',encoding='utf-8')) while True: data = self.request.recv(1024) if len(data) == 0:break print("[%s]says:%s"%(self.client_address,data.decode())) #self.request.sendall(data.upper()) cmd = subprocess.Popen(data.decode(),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) cmd_res = cmd.stdout.read() if not cmd_res: cmd_res = cmd.stderr.read() if len(cmd_res.strip()) == 0: cmd_res = bytes("cmd no msg",encoding='utf-8') self.request.sendall(cmd_res) if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('0.0.0.0',8009),MyServer) server.serve_forever() #永远接收 handle_server

import socket ip_port = ('127.0.0.1',8009) #买手机 s= socket.socket() #封装好TCP协议的 #拨号 s .connect(ip_port) welcome_msg = s.recv(1024) print("from server:",welcome_msg.decode()) while True: send_data = input(">>:").strip() if len(send_data) == 0:continue s.send(bytes(send_data,encoding='utf8')) #2.0可以发字符串,3.0只能发字节 #收消息 recv_data = s.recv(1024) print(str(recv_data,encoding='utf-8')) #挂断 s.close()
ftp上传实例:

#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Alex Li #!/usr/bin/env python # -*- coding:utf-8 -*- #import SocketServer import socketserver,json class MyServer(socketserver.BaseRequestHandler): def handle(self): # print self.request,self.client_address,self.server #conn = self.request.sendall(用send和sendall都可以) self.request.sendall(bytes('欢迎致电 10086,请输入1xxx,0转人工服务.',encoding="utf-8")) while True: data = self.request.recv(1024) #接收消息(动作,文件路径名,大小) if len(data) == 0:break #如果为空退出 print("data", data) print("[%s] says:%s" % (self.client_address,data.decode() )) task_data = json.loads(data.decode()) #接收到的字节信息转成字符串再转成字典 task_action = task_data.get("action") #获取动作 if hasattr(self, "task_%s"%task_action): #查看本程序里是否有这个函数成员 func = getattr(self,"task_%s" %task_action) #获取函数 func(task_data) #执行函数参数是字典信息,如果是tast_put那么执行tast_put(task_data)函数 else: print("task action is not supported",task_action) def task_put(self,*args,**kwargs): print("---put",args,kwargs) filename = args[0].get('filename') filesize = args[0].get('file_size') server_response = {"status":200} self.request.send(bytes( json.dumps(server_response), encoding='utf-8' ))#发送字典信息 f = open(filename,'wb') recv_size = 0 while recv_size < filesize: #接收字节小于文件字节 data = self.request.recv(4096) f.write(data) #接收内容写进文件 recv_size += len(data) print('filesize: %s recvsize:%s' % (filesize,recv_size)) print("file recv success") f.close() if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('0.0.0.0',8009),MyServer) server.serve_forever()

#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Alex Li import socket import os ,json ip_port=('192.168.11.150',8009) s=socket.socket() s.connect(ip_port) welcome_msg = s.recv(1024) #接收欢迎信息 print("from server:",welcome_msg.decode()) while True: send_data=input(">>: ").strip() if len(send_data) == 0:continue #是空重新输入 cmd_list = send_data.split() #将命令分割[put 123.txt] if len(cmd_list) <2:continue #小于2个元素重新输入命令 task_type = cmd_list[0] #获取动作 if task_type == 'put': #上传 abs_filepath = cmd_list[1] #获取上传的文件 if os.path.isfile(abs_filepath): #判断文件是否存在 #os.stat(path))获取一个文件(夹)信息,返回值是一个元组包含10个属性 file_size = os.stat(abs_filepath).st_size #获取文件大小 filename = abs_filepath.split("\\")[-1] #打印文件名和大小 print('file:%s size:%s' %(abs_filepath,file_size)) msg_data = {"action":"put", "filename":filename, "file_size":file_size} s.send(bytes(json.dumps(msg_data),encoding="utf-8")) #先转成字符串在转成字节发送 server_confirmation_msg = s.recv(1024) #接受服务器端发送的确认信息 confirm_data = json.loads(server_confirmation_msg.decode()) if confirm_data['status'] ==200: #确认信息 print("start sending file ",filename) f = open(abs_filepath,'rb') #字节方式打开 for line in f: s.send(line) #一行行发送 print("send file done ") else: print("\033[31;1mfile [%s] is not exist\033[0m" % abs_filepath) continue else: print("doesn't support task type",task_type) continue recv_data=s.recv(1024) print(str(recv_data,encoding='utf8')) s.close()
http://www.cnblogs.com/wupeiqi/articles/5040823.html