py6.6

#文件的上传和下载:
#自定义一个字典,让用户选择功能(上传/下载)。将要上传的文件路径传入,找到文件名,打开文件,计算文件大小,
#将功能、文件名、文件大小定义成一个字典,通过stuct模块和json模块固定字典的长度为4并序列化,以便防止发送
#时与后面要发送的文件内容出现黏包现象。通过循环将文件按照固定的大小(1024)分次发送到服务端。服务端也根据收到
#的文件大小来分次接收。
#struct模块
# import struct
# #
# r = struct.pack('i',10) #将10变成一个固定的长度4的字符串
# print(len(r))
# print(r)
# print(struct.unpack('i',r))#解开,得到的是一个元祖形式,原数值在元祖的第0个元素。

# import json
# dic = {'a': '1'}
# r = json.dumps(dic)
# print(json.loads(r))




server:

import socket
import struct
import json

sk = socket.socket()

sk.bind(('127.0.0.1',8080))

sk.listen(5)

conn , addr = sk.accept()

r  = conn.recv(4) #接收从客户端处已经被struct模块将原字典长度转换成固定长度的bytes(长度为4)
len_dic = struct.unpack('i',r)[0]#将这个bytes解开,变成一个元祖,要的是第一个元素-->原字典的长度。
# print(len_dic)
str_dic = conn.recv(len_dic).decode('utf-8')#根据原字典的长度来接收被序列化了字典
# print(str_dic)
str_dic = json.loads(str_dic)#将字典反序列化


if str_dic['opt'] == 'upload':
    name = 'new_' + str_dic['name']#防止重名,加一个前缀。
    with open(name,mode='wb') as f:#以bytes类型直接写进文件。
        file_size = str_dic['file_size']#获取传入文件的大小。
        while file_size:
            content = conn.recv(1024)#分次接收。
            f.write(content)         #接收一次写入一次。
            file_size -= len(content)#剩余的大小。

conn.close()#TCP协议两个套接字。
sk.close()

client:

import socket
import os
import json
import struct

sk = socket.socket()

sk.connect(('127.0.0.1',8080))

dic_c = {'1':'upload',
           '2':'download'
           }               #定义一个供用户选择的字典

while 1:
    num = input('请输入序号').strip()
    if num == '1':
        for k,v in dic_c.items():#将字典展示出来。
            print(k,':',v)
        path = input('请输入一个文件路径').strip()
        name = os.path.basename(path) #获取文件名,传入服务端,写入文件用。
        file_size = os.path.getsize(path)#获取文件大小,用来循环并分次传输。
        dic = {'opt': dic_c[num],'name':name,'file_size':file_size}#将功能、文件名、大小作为字典传入服务端。
        str_dic = json.dumps(dic) #将字典序列化,变成字符串,才能传输。
        len_dic = len(str_dic)  #计算序列化后的字典的长度。
        r = struct.pack('i',len_dic)#通过序列号后的字典的长度将其变成长度为固定4位的bytes.
        sk.send(r)                  #将固定后的bytes传入服务端。
        sk.send(str_dic.encode('utf-8'))#另一端已经解出了序列化后字典的长度,发送即可。
        with open(name,mode='rb') as f:#以bytes类型读取,就不用转码了。
            while file_size:
                content = f.read(1024)#分次读取
                sk.send(content)      #分次传输,直到传到没有为止。另一端也是与此同样来分次接收
                file_size -= len(content)
        break
sk.close()
文件上传

 

#执行系统命令:subprocess模块
# import subprocess
#
# res = subprocess.Popen('dir',shell=True,#告诉系统把'dir'当做系统命令来执行。
#                        stdout=subprocess.PIPE,#接收正确的结果
#                        stderr=subprocess.PIPE #接收错误的结果
#                        )
#
# print(res.stdout.read().decode('gbk'))#windows默认编码gbk,按照gbk解码读取。
# print('stderr:'+res.stderr.read().decode('gbk'))#没有错误结果,显示空。
#黏包现象:1.发送端与接收端的数据不对等,接收端能接收的字节<发送的数据,接收到一部分数据,下次再执行命令接收的时候再从上一次
#开始。接收端能接收的字节>发送的数据,如果数据较小,nagle算法会将数据较小及时间间隔较短的数据黏包,合并到一起一同接收。
#如果没有合理的拆包机制,则造成了无法拆分,出现黏包现象。
#tcp是字节流的形式。不能有空消息,如果有空消息,流中就会出现错误。造成无法发送。需要有空消息处理机制。
#udp是数据报形式,可以有空消息。
#tcp有黏包现象,udp没有。udp是一个sendto对应一个recvfrom,一条消息对应一条消息,如果接收的字节数不够,那其余的数据
#就会丢失。所以不可靠。不会黏包。
黏包

 

#自己创建一个模块,通过调用此模块,省略一些编码解码的步骤。
# from socket import *
#
# class My_socket(socket): #以socket作为父类。
#     def __init__(self,coding = 'utf-8'):#编码为默认关键字参数。
#         self.coding = coding
#         super(My_socket, self).__init__(type=SOCK_DGRAM) #没有socket.
#
#     def my_recv(self,num):  #传进来接收的字节数。如1024
#         msg_r ,addr = self.recvfrom(num)
#         return msg_r.decode(self.coding),addr #返回编好码的内容,按照传进来的或默认的编码方式。
#
#     def my_send(self,msg_s,addr):
#         return self.sendto(msg_s.encode(self.coding),addr)#返回编好码的要发送的内容。

server:
from 练习 import My_socket

sk = My_socket() #实例化一个套接字对象。

sk.bind(('127.0.0.1',8090))#正常绑定

while 1:
    msg_r , addr = sk.my_recv(1024) #调用类中接收方法。
    print(msg_r)            #不需要做解码动作。

    msg_s = input('>>>')
    sk.my_send(msg_s,addr)  #调用发送函数。不需要编码。

sk.close()

client:

from 练习 import My_socket

sk = My_socket()

while 1:
    msg_s = input('>>>')
    sk.my_send(msg_s,('127.0.0.1',8090))

    msg_r ,addr = sk.my_recv(1024)
    print(msg_r)

sk.close()
自定义socket

 

#编码流程:
#    TCP                                       udp
#     s           c                             s                  c
# 创建套接字                               创建套接字
# 绑定套接字                               绑定套接字
# 监听
# 等待接收      连接
# 发送/接收                               先接收/再发送
# 关闭c套接字
# 关闭s套接字    关闭s套接字              关闭套接字           关闭套接字
TCP/UDP编码流程

 

posted @ 2018-06-06 20:49  消暑酸梅粉  阅读(141)  评论(0编辑  收藏  举报