python之day9(socket)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python之day9(socket)相关的知识,希望对你有一定的参考价值。

今日重点socket网络编程:

1,tcp/ip简介:

2,socket简单应用模型:

3,socket单用户模式扩展:

4,socketserver实例:

 

首先记录一个在day6时没有记录的知识点:

百分比的应用:

import sys
import time

def view_bar(num, total):
    rate = float(num) / float(total)
    rate_num = int(rate * 100)
    r = ‘\\r%d%%‘ % (rate_num, )
    sys.stdout.write(r)
    sys.stdout.flush()


if __name__ == ‘__main__‘:
    for i in range(0, 101):
        time.sleep(0.1)
        view_bar(i, 100)

 

进入主要内容:

一,tcp/ip

 

 

技术分享

    osi7层结构以及具体作用。

    物理层:电缆,双绞线,无线电波

    链路层(网卡):ethernet通用协议---》找到唯一的mac地址

    网络层:IP 找到了子网中唯一的标识。

    传输层:tcp,udp,port 规定传输协议以及端口号。

 

技术分享

    tcp/ip基本模型

 

 

二,socket简单应用模型:

技术分享

    socket的所在位置。

 

技术分享

    整个的socket交流过程。

 

socket要点提示:

1.基于python3.5.2版本的socket只能收发字节(python2.7可以发送str)
2.退出只在客户端退出就ok了
3.s.accept()和s.recv()是阻塞的(基于链接正常)
4.listen(n) n代表:能挂起的链接数,如果n=1,代表可以链接一个,挂起一个,第三个拒绝
5.服务端出现端口冲突:修改监听端口号
6.服户端:1.send #数据长度
              4.recv #收到确认信息,开始下一步发送

          send

  客户端:2.recv #获取数据长度
             3.send #发送确认信息

          recv #循环接收

 

基础socket程序:

技术分享
 1 import socket
 2 ip_port=(127.0.0.1,9999)
 3 #买手机
 4 s=socket.socket()
 5 #买手机卡
 6 s.bind(ip_port)
 7 #开机
 8 s.listen(5)
 9 #等待电话
10 while True:
11     conn,addr=s.accept()
12     #收消息
13     while True:
14         try:
15             recv_data=conn.recv(1024)
16             if len(recv_data) == 0:break
17             # print(‘--------------------‘,type(recv_data))
18             #发消息
19             send_data=recv_data.upper()
20             print(send_data)
21             conn.send(send_data)
22         except Exception:
23             break
24     #挂电话t
25     conn.close()
server1
技术分享
 1 #!/usr/bin/env  python
 2 # -*- coding: UTF-8 -*-
 3 # Author: Aaron Shen
 4 
 5 import socket
 6 ip_port=(127.0.0.1,9999)
 7 #买手机
 8 s=socket.socket()
 9 #拨号
10 s.connect(ip_port)
11 #发送消息
12 while True:
13     send_data=input(">>: ").strip()
14     if send_data == "exit": break
15     if len(send_data) == 0:continue
16     s.send(bytes(send_data,encoding=utf8))
17 
18 
19     #收消息
20     recv_data=s.recv(1024)
21     print(str(recv_data,encoding=utf8))
22 #挂电话
23 s.close()
client1

 

三,socket单用户模式扩展:

 

关于执行系统命令,以及粘包问题解决思想:

执行系统命令 用到subprocess 模块 .Popen方法 参数跟(系统命令,shell=True,stdout = subprocess.PIPE, stderr = subprocess.PIPE)

粘包问题产生的原因:

由于发送端的字节数超过了接收端一次性可接收的数量,到时一次无法将发送端的数据全部接收完,再继续执行命令是会继续进行发送,造成粘包。

解决办法:

首先就是要有一下循环的思想,只要没接收完就一直接收。因此我们要知道发送包data的具体大小。

其次我们要先将data的大小发送给接收端。

然后接收端回复Ok我收到了发送端要发送多大的包给我,我会根据这个包的大小进行循环接收,接收完就不再接收。

最后发送端开始发送数据,接收端接收知道接收完成。

 

技术分享
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Alex Lire
 4 import socket
 5 import subprocess #导入执行命令模块
 6 ip_port=(127.0.0.1,9999) #定义元祖
 7 #买手机
 8 s=socket.socket()  #绑定协议,生成套接字
 9 s.bind(ip_port)    #绑定ip+协议+端口:用来唯一标识一个进程,ip_port必须是元组格式
10 s.listen(5)        #定义最大可以挂起胡链接数
11 #等待电话
12 while True:  #用来重复接收新的链接
13     conn,addr=s.accept()   #接收客户端胡链接请求,返回conn(相当于一个特定胡链接),addr是客户端ip+port
14     #收消息
15     while True: #用来基于一个链接重复收发消息
16             try: #捕捉客户端异常关闭(ctrl+c)
17                 recv_data=conn.recv(1024) #收消息,阻塞
18                 if len(recv_data) == 0:break #客户端如果退出,服务端将收到空消息,退出
19 
20                 #发消息
21                 p=subprocess.Popen(str(recv_data,encoding=utf8),shell=True,stdout=subprocess.PIPE) #执行系统命令,windows平
22                                                                                                       # 台命令的标准输出是gbk编码,需要转换
23                 res=p.stdout.read()   #获取标准输出
24                 if len(res) == 0:   #执行错误命令,标准输出为空,
25                     send_data=cmd err
26                 else:
27                     send_data=str(res,encoding=gbk)  #命令执行ok,字节gbk---->str---->字节utf-8
28 
29                 send_data=bytes(send_data,encoding=utf8)
30 
31 
32                 #解决粘包问题
33                 ready_tag=Ready|%s %len(send_data)
34                 conn.send(bytes(ready_tag,encoding=utf8)) #发送数据长度
35                 feedback=conn.recv(1024)  #接收确认信息
36                 feedback=str(feedback,encoding=utf8)
37 
38                 if feedback.startswith(Start):
39                     conn.send(send_data)  #发送命令的执行结果
40             except Exception:
41                 break
42     #挂电话
43     conn.close()
进阶socket_server
技术分享
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Alex Li
 4 import socket
 5 ip_port=(127.0.0.1,9999)
 6 #买手机
 7 s=socket.socket()
 8 #拨号
 9 s.connect(ip_port)  #链接服务端,如果服务已经存在一个好的连接,那么挂起
10 
11 while True:        #基于connect建立的连接来循环发送消息
12     send_data=input(">>: ").strip()
13     if send_data == exit:break
14     if len(send_data) == 0:continue
15     s.send(bytes(send_data,encoding=utf8))
16 
17     #解决粘包问题
18     ready_tag=s.recv(1024) #收取带数据长度的字节:Ready|9998
19     ready_tag=str(ready_tag,encoding=utf8)
20     if ready_tag.startswith(Ready):#Ready|9998
21         msg_size=int(ready_tag.split(|)[-1])  #获取待接收数据长度
22     start_tag=Start
23     s.send(bytes(start_tag,encoding=utf8)) #发送确认信息
24 
25     #基于已经收到的待接收数据长度,循环接收数据
26     recv_size=0
27     recv_msg=b‘‘
28     while recv_size < msg_size:
29         recv_data=s.recv(1024)
30         recv_msg+=recv_data
31         recv_size+=len(recv_data)
32         print(MSG SIZE %s RECE SIZE %s %(msg_size,recv_size))
33 
34     print(str(recv_msg,encoding=utf8))
35     #挂电话
36 s.close()
进阶socket_client

 

 

四,socketserver实例:

  这部分是alex大拿老师讲的,基本上没听懂,呵呵,主要靠课下重新读代码,重新构思的。

  首先说一下socketserver与普通socket的区别:

  普通socket所有点链路(conn)都是单通道的,只能一个客户端占用。服务端也是一对一进行相应。如果还有别的客户端要访问就要排队等待,直到之前的客户端退出后才可以与服务端进行交互。

  

  socketserver 使用socketserver模块(2.7头字母要进行大写),继承了socketserver.BaseRequestHandler这个类,我们用的时候必须使用 handle这个函数。他的特点就是可以完成一对多的相应。

   基本程序构建:

技术分享
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Alex Li
 4 
 5 
 6 #!/usr/bin/env python
 7 # -*- coding:utf-8 -*-
 8 #import SocketServer
 9 import socketserver
10 
11 class MyServer(socketserver.BaseRequestHandler):
12     def handle(self):
13         # print self.request,self.client_address,self.server
14         self.request.sendall(bytes(欢迎致电 10086,请输入1xxx,0转人工服务.,encoding="utf-8"))
15         while True:
16             data = self.request.recv(1024)
17             print("-->",len(data))
18             if len(data) == 0:break
19             print("[%s] says:%s" % (self.client_address,data.decode() ))
20             self.request.sendall(data.upper())
21 
22 if __name__ == __main__:
23     server = socketserver.ThreadingTCPServer((127.0.0.1,8009),MyServer)
24     server.serve_forever()
socket_server
技术分享
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Alex Li
 4 
 5 import socket
 6 ip_port=(192.168.11.58,8009)
 7 #买手机
 8 s=socket.socket()
 9 #拨号
10 s.connect(ip_port)
11 #发送消息
12 welcome_msg = s.recv(1024)
13 print("from server:",welcome_msg.decode())
14 while True:
15     send_data=input(">>: ").strip()
16     if len(send_data) == 0:continue
17     s.send(bytes(send_data,encoding=utf8))
18     #收消息
19     recv_data=s.recv(1024)
20     print(str(recv_data,encoding=utf8))
21     #挂电话
22 s.close()
socket_client

 

  通过socket 远程执行命令(通过客户端发送命令到服务端并执行)

技术分享
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Alex Li
 4 
 5 
 6 #!/usr/bin/env python
 7 # -*- coding:utf-8 -*-
 8 #import SocketServer
 9 import socketserver
10 import subprocess 
11 class MyServer(socketserver.BaseRequestHandler):
12     def handle(self):
13         # print self.request,self.client_address,self.server
14         self.request.sendall(bytes(欢迎致电 10086,请输入1xxx,0转人工服务.,encoding="utf-8"))
15         while True:
16             data = self.request.recv(1024)
17             if len(data) == 0:break
18             print("[%s] says:%s" % (self.client_address,data.decode() ))
19             #self.request.sendall(data.upper())
20             cmd = subprocess.Popen(data.decode(),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
21             cmd_res =cmd.stdout.read()
22             if not cmd_res:
23                cmd_res = cmd.stderr.read()
24             if len(cmd_res) == 0: #cmd has not output 
25                cmd_res = bytes("cmd has output",encoding="utf-8")
26             self.request.send(cmd_res )
27 
28 
29 if __name__ == __main__:
30     server = socketserver.ThreadingTCPServer((0.0.0.0,8009),MyServer)
31     server.serve_forever()
com_server
技术分享
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Alex Li
 4 
 5 import socket
 6 ip_port=(192.168.11.58,8009)
 7 #买手机
 8 s=socket.socket()
 9 #拨号
10 s.connect(ip_port)
11 #发送消息
12 welcome_msg = s.recv(1024)
13 print("from server:",welcome_msg.decode())
14 while True:
15     send_data=input(">>: ").strip()
16     if len(send_data) == 0:continue
17     s.send(bytes(send_data,encoding=utf8))
18     #收消息
19     recv_data=s.recv(1024)
20     print(str(recv_data,encoding=utf8))
21     #挂电话
22 s.close()
com_client

 

  通过socket进行ftp 上传操作(通过客户端发送指令,上传到服务器端)

技术分享
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Alex Li
 4 
 5 
 6 #!/usr/bin/env python
 7 # -*- coding:utf-8 -*-
 8 #import SocketServer
 9 import socketserver,json
10 class MyServer(socketserver.BaseRequestHandler):
11     def handle(self):
12         # print self.request,self.client_address,self.server
13         self.request.sendall(bytes(欢迎致电 10086,请输入1xxx,0转人工服务.,encoding="utf-8"))
14         while True:
15             data = self.request.recv(1024)                  #接收要进行操作的基本信息,包括动作,文件路径以及文件名,文件大小
16             if len(data) == 0:break
17             print("data", data)
18             print("[%s] says:%s" % (self.client_address,data.decode() ))
19 
20             task_data = json.loads( data.decode()  )        #接收到一个json格式的字典
21             task_action = task_data.get("action")           #获取字典的中动作的值
22             if hasattr(self, "task_%s"%task_action):        #反射到本程序的下面的函数,查看是否存在。如果存在
23                func = getattr(self,"task_%s" %task_action)  #获取函数
24                func(task_data)                                     #执行函数,同时参数就是那个字典
25             else:
26                print("task action is not supported",task_action)
27 
28     def task_put(self,*args,**kwargs):
29         print("---put",args,kwargs)         
30         filename = args[0].get(filename)          #文件名就等于task_data.get(‘filename‘)
31         filesize = args[0].get(file_size)         #文件大小就等于task_data.get(‘file_size‘)
32         server_response = {"status":200}            #准备工作都准备好后,给客户端发一个确认
33         self.request.send(bytes( json.dumps(server_response), encoding=utf-8  ))      #发送确认
34         f = open(filename,wb)                     #打开文件
35         recv_size = 0                                #未开始接收,因此接收大小是 0
36         while recv_size < filesize:                  #如果接收大小小于文件大小就是一直循环
37             data = self.request.recv(4096)            #开始接收
38             f.write(data)                               #写入文件
39             recv_size += len(data)                      #增加大小
40             print(filesize: %s  recvsize:%s % (filesize,recv_size))           #展示进度
41         print("file recv success")                      #循环结束,完成接收
42         f.close()
43 
44 if __name__ == __main__:
45     server = socketserver.ThreadingTCPServer((0.0.0.0,8009),MyServer)
46     server.serve_forever()
ftp_server
技术分享
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # Author:Alex Li
 4 
 5 import socket
 6 import os ,json
 7 ip_port=(192.168.11.150,8009)                     #要访问的服务端IP以及端口
 8 #买手机
 9 s=socket.socket()                                      #生成套接字s
10 #拨号
11 s.connect(ip_port)                                      #连接服务和端口
12 #发送消息
13 welcome_msg = s.recv(1024)                              #获取服务端相应
14 print("from server:",welcome_msg.decode())             #打印获取的服务端欢迎语
15 while True:                                             #循环交互
16     send_data=input(">>: ").strip()
17     if len(send_data) == 0:continue                     #如果发送为空就重新发送
18 
19     cmd_list = send_data.split()                        #将命令进行分割 [put  test.txt]
20     if len(cmd_list) <2:continue                        #如果小于2个元素就重新输入,命令是不完整的
21     task_type = cmd_list[0]                               #第一个是命令 put, 第二个是文件路径
22     if task_type == put:                              #如果是 put
23         abs_filepath = cmd_list[1]                       #第二个就是文件路径
24         if os.path.isfile(abs_filepath):                   #如果文件存在
25             file_size = os.stat(abs_filepath).st_size       #获取文件大小
26             filename = abs_filepath.split("\\\\")[-1]         #获取文件名
27             print(file:%s size:%s %(abs_filepath,file_size))
28             msg_data = {"action":"put",
29                         "filename":filename,
30                         "file_size":file_size}             #写成字典包括动作,文件名,文件大小
31 
32             s.send(  bytes(json.dumps(msg_data),encoding="utf-8")  )        #将字典整合成json发送给服务端(明确要上传的信息)
33             server_confirmation_msg = s.recv(1024)                           #接收服务端发送的确认信息。
34             confirm_data = json.loads(server_confirmation_msg.decode())      #获取确认信息,服务端发来的信息也是json封装的字典
35             if confirm_data[status] ==200:                                #如果是200 确认成功
36 
37                 print("start sending file ",filename)
38                 f = open(abs_filepath,rb)                                 #二进制打开要上传的文件
39                 for line in f:                                              #逐行循环
40                     s.send(line)                                             #上传到服务端   
41 
42                 print("send file done ")
43 
44         else:
45             print("\\033[31;1mfile [%s] is not exist\\033[0m" % abs_filepath)
46             continue
47     else:
48         print("doesn‘t support task type",task_type)
49         continue
50     #s.send(bytes(send_data,encoding=‘utf8‘))

以上是关于python之day9(socket)的主要内容,如果未能解决你的问题,请参考以下文章

python day9 socket编程

初学python之day9

Python 之 Socket编程(TCP/UDP)

day9

Python之路第一课Day9--随堂笔记之二(进程线程协程篇)

python基础-day9-函数