python socket编程 day9


首先说一下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()
socket_server
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()
socket_client

解决粘包问题:

服务端: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()
sk_server
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()
sk_client

总结: 

  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
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()
handle_client

 

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()
ftp_server
#!/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()
ftp_client

 http://www.cnblogs.com/wupeiqi/articles/5040823.html

posted on 2016-07-04 14:23  QinLing  阅读(466)  评论(0编辑  收藏  举报

导航