网络学习01-网络编程

1 网络基础相关的知识

  1. 架构
  1. C / S架构 : client客户端 和 server服务器端 

优势 : 能充分发挥PC机的性能

  1. B / S架构 : browser浏览器 和 server服务器   隶属于C/S架构

B / S架构 统一了应用的接口.

  1. 通信的事:
  1. 同一台电脑上两个py程序通信  : 打开一个文件
  2. 两个电脑如何通信 : 连一个网线
  3. 多个电脑通信 :

  ex : 电脑1(源)要找电脑2(目标)

  电脑1首先发送一个请求帧,期中包含(我的ip是192.168.1.1,我的mac地址是xxxxxxxx,我要找ip地址为192.168.1.2的主机),将此请求发送给交换机.

  交换机要广播这条消息给其他所有的主机

  目标主机接收到消息后,对比发现自己就是被找的主机,回复给交换机信息(我的ip地址是192.168.1.2,我的mac地址是yyyyyyyyy,请回复给ip地址为192.168.1.1,mac地址为xxxxxxx的主机)

  交换机单播形式返回给源主机

             

知识点 :

   1  mac地址 : 是一个物理地址,全球唯一,  类似于身份证

   2  ip地址:    是一个四位点分十进制,它标识了计算机在网络中的位置.类似于 学号

   3  交换机的通信方式:

         广播 : 吼一嗓子

         单播 : 一对一

         组播 : 一对多

   4  arp协议通过目标ip地址获取目标mac地址的一个协议.

   5  端口 : 操作系统为本机上每一个运行的程序都随机分配一个端口,其他电脑上的程序可以通过端口获取到这个程序

      ip地址 + 端口 能唯一找到某台电脑上的某一个服务程序

   6  路由器 : 连接不同网段 , 路由

   7  网关   : 类似于一个局域网的出口和入口

   8  网段   : 一个局域网内的ip地址范围

   9  子网掩码 : 子网掩码 &  ip地址  得到网段

   10  osi 五层模型:

应用层       :  http,https,ftp

传输层       :  tcp / udp          四层交换机  四层路由器

网络层       :  ip协议            路由器  三层交换机

数据链路层   :  arp协议           以太网交换机  网卡  网桥

物理层       :  传输电信号        集线器  网线   光纤

 

SOCKET层

AF_INET:基于网络类型的套接字。

TCP:安全可靠的连接。

UDP:速度快,不可靠。

服务端的代码

import socket

sk=socket.socket()#不传参数,默认使用基于网络类型的套接字,协议:TCP

sk.bind((‘192.168.12.104’,8080))  #端口的范围是0-65535,但是0-1023不要使用,为系统默认使用

sk.listen()#同时能接收的连接

conn,addr = sk.accept()#等待接收客户端的连接 ,阻塞等待

print('addr:' , conn)

print('addr:' ,addr)

time.sleep(20)

conn.close()

sk.close()

 

客户端的代码

import socket

import time

sk = socket。socket()

sk。connect(‘192.168.12.104’,8080)

time.sleep(20)

 

第2次通信

 

服务端的代码

import socket

sk=socket.socket()#我买一个新手机

sk.bind((‘192.168.12.104’,8080))  #我买一个手机卡

sk.listen()#开机

conn,addr = sk.accept()#等待朋友给我打电话

msg=conn.recv(2048) #接收数据,接收10个字节

print(msg.decode('utf-8'))

conn.close()#挂断电话

sk.close()#关机

客户端的代码

import socket

import time

sk = socket。socket()

sk。connect(‘192.168.12.104’,8080)#连接

sk。send(b“message”)#

sk。send(“中文”.encode("utf-8"))

time.sleep(20)

 

第二节课程

UDP协议

服务器端

import socket 

sk=socket.socket(type=socket.SOCK_DGRAM)

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

#进行收发

while 1:

    msg_r,addr=sk.recvfrom(1024)  #这里为什么用两个参数,因为如果是一个参数的话,这个参数里面包含ip端口号等,是元组                                                           形式的。

    print(msg_r.decode('utf-8'))

    msg_s = input('>>>>')

    sk.sendto(msg_s.encode('utf-8'),addr)

sk.close()

 

客户端

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

name=input(“请输入你的名字:”)

while 1:

    msg_s = input('>>>>')

    info = name + ":" + msg_s

    sk.sendto(info.encode('utf-8'),('127.0.0.1',8090))

    msg_r,addr = sk.recvfrom(1024)

    print(msg_r.decode('utf-8'))

sk.close()

UDP:允许一个服务器同时和多个客户端通信

自定义类继承socket类 My_UDP.py

import socket

class Mysocket(socket.socket):    #继承自socket文件中的socket类,此时socket就是父类

    def __init__(self,encoding="utf-8"):

        self.encoding = encoding

        super(Mysocket,self).__init__(type=socket.SOCK_DGRAM)  #执行父类socket中的__init__方法

    def my_sendto(self,msg,addr):

        return self.sendto(msg.encode(self.encoding),addr)            #调用父类中的sendto方法

    def my_recvfrom(self,num):

        msg_r,addr=self.recvfrom(num)                              #调用父类中的recvfrom方法

        return msg_r.decode(self.encoding),addr   

服务端程序

from My_UDP import Mysocket

sk = Mysocket()

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

msg,addr = sk.my_recvfrom(1024)

print(msg)

sk.close()

客户端程序

from My_UDP import Mysocket

sk = Mysocket()

sk.my_sendto("abc",('127.0.0.1',8080))

sk.close()

 

 

 

 

 

 

第三节  执行命令

粘包问题就是数据混乱问题。

            发送端发送数据,接收端不知道应该如何去接收,造成的一种数据混乱的现象

在TCP协议中,有一个合包机制(nagle算法),将多次连续发送且间隔较小的数据,进行打包成一块数据传送。

还有一个机制,拆包机制。在发送端,因为受到网卡MTU限制,会将大的超过MTU限制的数据进行拆分,拆分成多个

小的数据进行传输。当传输到目标主机的操作系统层时,会重新将多个小的数据合并成原本的数据。

 

 

只有TCP协议才会发生粘包,UDP协议不会。UDP协议本层对一次收发数据大小的限制是:

65535-ip包头(20)-UDP包头(8)=65507

站在数据链路层,因为网卡的mtu一般被限制在1500,所以对于数据链路层,一次收发数据的大小被限制在

1500-ip包头(20)-udp包头(8)=1472

得到结论:如果sendto(num)num>65507 报错

1472<num<65507 ,会在数据链路层拆包,而UDP本身就是不可靠协议,所以一旦拆包之后,造成的多个小数据包

在网络传输中,如果丢任何一个,那么此次数据传输失败。

num<1472,是比较理想的状态。

 

在python代码中,如何去调用操作系统的命令

import os

r = os.popen('dir')  #执行操作系统的命令

print(r.read())  #打印出来的是目录

新模块:subprocess

import subprocess

r = subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)

                       #subprocess.Popen(cmd,shell=True,subprocess.stdout,subprocess.stderr)

                       cmd: 代表系统命令

                       shell=True 代表这个命令是系统命令,告诉操作系统将cmd当成系统命令去执行

                      stdout 是执行完系统命令之后,用于保存结果的一个管道

                     stderr 是执行完系统命令之后,用于保存错误结果的一个管道

stdout=r.stdout.read().decode('gbk')

stderr=r.stderr.read().decode(“gbk”)

print('正确的返回结果:',stdout)

print('错误的返回结果:',stderr)

 

客户端代码

import socket

sk=socket。socket()

sk。connect((‘127.0.0.1’,8080))

while 1:

    cmd=input(“请输入一个命令》》》”)

    sk。send(cmd。encode(“utf-8”))

    result = sk。recv(1028)。decode(‘gbk’)

sk。close()

服务器端代码

import socket

import subprocess

sk = sk。socket()

sk。bind((‘127.0.0.1’,8080))

sk。listen()

conn,addr = sk。accept()

while 1:

    cmd = conn。recv(1024).decode(“utf-8”)

    r = subprocess。Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)

    stdout=r.stdout.read().decode('gbk')

    stderr=r.stderr.read().decode(“gbk”)

   if not stderr:  #如果stderr没有内容,此时bool=false

        conn.send(stdout)

    else:

       conn.send(stderr)

conn.close()

sk.close()

演示粘包

service

import socket

sk = socket.socket()

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

sk.listen()

conn,addr=sk.accept()

conn.send(b'hello')

conn.send(b'world')

conn.close()

sk.close()

client

import socket

sk = socket.socket()

sk.connect_ex(('127.0.0.1,8888'))

msg1=sk.recv(1024)

msg2=sk.recv(1024)

print(msg1)

print(msg2)

sk.close()

文件的上传下载

service

import socket

import json

sk = socket.socket()

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

sk.listen()

conn,addr=sk.accept()

str_dic = conn.recv(9090).decode('utf-8')

dic = json.loads(str_dic)              #    dic = {'opt':menu.get(num),'filename':None,'content':None}

if dic['opt'] == 'upload':

    #上传

    filename = ‘1’ + dic[filename]

    with open(filename,'w',encoding='utf-8') as f:

        f.write(dic['content'])

elif dic['opt'] == 'download':

    #下载

 

conn.close()

sk.close()

client

import socket

import os

import json

sk = socket.socket()

s = sk.connect_ex('127.0.0.1',8080)#带返回值,如果出错,不会报错,会返回错误的编码。

menu= {‘1’:‘upload’,‘2’:‘download’}

for k,v in menu.items():

    print(k,v)

num = input('请输入功能选项:')

if num == '1':

    #上传功能

    dic = {'opt':menu.get(num),'filename':None,'content':None}

    file_path=input('请输入一个文件的绝对路径》》》')                  #'D:/sylar/python/day21/__init__.py

    filename = os.path.basename(file_path)

    with open (file_path,'r',encoding='utf-8') as f:

        content = f.read()

    dic['filename'] = filename

    dic['content'] = content

    str_dic = json.dumps(dic)

    sk.send(str_dic.encode('utf-8'))

 

 

 

 

 

 

elif num =='2':

    #下载功能

else:

    print(“错误”)

 

 

 

sk.close()

 

实现小文件的传输

service端

import socket

import json

sk = socket.socket()

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

sk.listen()

conn,addr=sk.accept()

str_dic = conn.recv(9090).decode('utf-8')

dic = json.loads(str_dic)

if dic[''opt''] == ''upload'':

    filename = ''1''+dic[''filename'']

with open (filename,''w'',encoding=''utf-8'') as f:

    f.write(dic[''content''])

elif dic[''opt''] ==''download'':

    pass

conn.close()

sk.close()

client端

import  socket

import os

import json

sk = socket.socket()

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

menu={''1'':''upload'',''2'':''download''}

for k ,v in menu.items():

    print(k,v)

num = input(''请输入功能选项:'')

if num == ''1'':

    dic = {''opt'':menu.get(num),''filename'':None,''content'':None}

    file_path = input(''请输入一个绝对路径'')

    filename=os.path.basename(file_path)

    with open(file_path,''r'',encoding=''utf-8'') as f:

        content=f.read()

    dic[''filename''] = filename

    dic[''content''] = content

    str_dic = json.dumps(dic)  #把字典序列化成字符串形式的字典

    sk.send(str_dic.encoding(''utf-8''))

elif num == '2':

    pass

 

大文件传输

client端

import  socket

import os

import json

sk = socket.socket()

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

menu={''1'':''upload'',''2'':''download''}

for k ,v in menu.items():

    print(k,v)

num = input(''请输入功能选项:'')

if num == ''1'':

    dic = {''opt'':menu.get(num),''filename'':None,''filesize'':None}

    file_path = input(''请输入一个绝对路径'')  #文件的绝对路径

    filename=os.path.basename(file_path)   #文件的名字

    filesize=os.path.getsize(file_path)        #获取用户输入的路径中文件的大小

   

    dic[''filename''] = filename

    dic[''filesize''] = filesize

    str_dic = json.dumps(dic)  #把字典序列化成字符串形式的字典

    sk.send(str_dic.encoding(''utf-8'')) #将被填充完成的字典先发送给服务器

    sk.recv(1024)   #为什么要有一个recv,因为上边send字典时,如果程序执行过快,可能会马上执行到下边的                                                          send(content),此时有可能发生粘包,所以在中间加一个recv,

     with open(file_path,''rb'') as f:

        while filesize:

            content = f.read(1024)

            sk.send(content)

            filesize -= len(content)

elif num == '2':

    pass

service端

import socket

import json

sk = socket.socket()

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

sk.listen()

conn,addr=sk.accept()

str_dic = conn.recv(100).decode('utf-8')        #    str_dic = {''opt'':menu.get(num),''filename'':None,''filesize'':None}

conn.send(b'ok')

dic = json.loads(str_dic)

if dic[''opt''] == ''upload'':

    filename = ''1''+dic[''filename'']

    with open (filename,''ab'') as f:

     while dic['filesize']:

         content = conn.recv(1024)

          f.write(content)

         dic['filesize'] -= len(content)

elif dic[''opt''] ==''download'':

    pass

conn.close()

sk.close()

 

优化

client端

import  socket

import os

import json

import struct

sk = socket.socket()

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

menu={''1'':''upload'',''2'':''download''}

for k ,v in menu.items():

    print(k,v)

num = input(''请输入功能选项:'')

if num == ''1'':

    dic = {''opt'':menu.get(num),''filename'':None,''filesize'':None}

    file_path = input(''请输入一个绝对路径'')  #文件的绝对路径

    filename=os.path.basename(file_path)   #文件的名字

    filesize=os.path.getsize(file_path)        #获取用户输入的路径中文件的大小

   

    dic[''filename''] = filename

    dic[''filesize''] = filesize

    str_dic = json.dumps(dic)  #把字典序列化成字符串形式的字典

    len_dic = len(str_dic)    #获取到字典的长度,是一个int类型的数据

    b_len_dic = struct.pack('i',len_dic)  #将len_dic字典打包成bytes类型,bytes类型的数据可以使用+指令,用一个4bytes的数据                                                              表示字典的长度

    sk.send(b_len_dic + str_dic.encoding(''utf-8'')) #将bytes类型的长度加上bytes类型的数据内容,一起发给服务器

                               

     with open(file_path,''rb'') as f:

        while filesize:

            content = f.read(1024)

            sk.send(content)

            filesize -= len(content)

elif num == '2':

    pass

service端

import socket

import json

import struct

sk = socket.socket()

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

sk.listen()

conn,addr=sk.accept()

b_len_dic = conn.recv(4)       #   接收到的是bytes类型的 字典的长度

len_dic = struct.unpack('i',b_len_dic)[0] #获取到int类型字典的长度,unpack得到的是一个元组,要取下标为0的位置

str_dic = conn.recv(len_dic).decode('utf-8')    #根据字典的长度去接收字典的数据

conn.send(b'ok')

dic = json.loads(str_dic)

if dic[''opt''] == ''upload'':

    filename = ''1''+dic[''filename'']

    with open (filename,''ab'') as f:

     while dic['filesize']:

         content = conn.recv(1024)

          f.write(content)

         dic['filesize'] -= len(content)

elif dic[''opt''] ==''download'':

    pass

conn.close()

sk.close()

 

 

切换目录

service

import socket

import os

sk = socket.socket()

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

sk.listen()

conn,addr=sk.accept()

def send_data(conn,path):   #把对应的路径转换成字符串发送

     list_dir = os.listdir(path)

    str_dir = '--'.join(list_dir)

    conn.send(str_dir.encode('utf-8')

abs_path = conn.recv(1024).decode('utf-8')  #获取用户输入的绝对路径

current_dir = abs_path + '/'                       #以下在处理,都要根据当前路径去处理,无论是返回上一层,还是进入下一个文件夹

send_data(conn,current_dir)                   #把用户输入的路径下的所有文件及文件夹返回给客户端

while 1:

    cmd =conn.recv(1024).decode('utf-8')

    if cmd == ‘..’:

        current_dir =current_dir.split('/')[:-1]   #[:-1] 的作用是把空字符串去掉

        current_dir.pop()

        current_dir = '/'.join(current_dir)+‘/’  #在windows操作系统中,/和./是代表当前目录,  ../是代表上一级目录

       

        send_data(conn,current_dir)

    else:

         filename = cmd.split(' ')[1]              #获取用户输入的文件名字

          current_dir  += filename + '/'          #将文件名字添加到当前路径下,组成一个完整的新路径

           if os.path.isdir(current_dir):           #如果客户输入的文件名字是一个文件夹

               send_data(conn,current_dir)

            else:                                               #如果不是文件夹

               conn.send(b'不是文件夹')

               

         

        

    

 

 

 

conn.close()

sk.close()

 

 

client

import socket

import os

sk = socket.socket()

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

abs_path = input('请输入您的根目录:')

sk.send(abs_path.encode('utf-8'))

current_dir = sk.recv(1024).decode('utf-8')

print(current_dir.split('--'))

while 1:

  cmd = input('请输入》》‘)   #指令为cd 文件夹 或者 。。

  if cmd == '..':

     sk.send(cmd.encode('utf-8'))

     current_dir = sk.recv(1024).decode('utf-8')

      print(current_dir.split('--'))

if cmd == ‘cd':

      filename = input("请输入一个文件夹名”)

      sk.send((cmd +' ' + filename).encode('utf-8'))

       current_dir = sk.recv(1024).decode('utf-8')

      print(current_dir.split('--'))

sk.close()

 

SOCKETSERVER模块

主要是为了解决TCP协议中,服务器不能同时连接多个客户端的问题。

是处于socket抽象层和应用层之间的一层,比socket更贴近用户。

server端:

import socketserver

class MySocket(socketserver.BaseRequestHandler):

   def handle(self):      #这个方法的名字是固定的,必须是这个名字

        #收发的逻辑代码,其中self.request就等效于conn

      msg = self.request.send()

       self.request.recv(1024).decode('utf-8')

      self.request.send(msg.upper().encode('utf-8'))

         pass

server = socketserver.TCPServer((‘127.0.0.1’,8080),MySocket) #固定的

server.serve_forever()   #开启一个永久性的服务

client端:

import socket 

sk = socket。socket()

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

msg_s = input('>>>>')

sk.send(msg_s.encode('utf-8'))

print(sk.recv(1028).decode('utf-r'))

sk.close()

用户三次登录验证

server

import socketserver

import json

import hashlib

class Mysocket(socketserver.BaseRequestHandler):

     def handle(self):

         sor=b'wusir'

        str_dic = self.request.recv(1024).decode('utf-8')

        dic = json.loads(str_dic)

        if not dic['status']:           #   '''没有登录成功的情况'''

           with open ('info0,'r',encoding='utf-8') as f:

                for info in f:

                     username,password = info.strip().split('|')

                      if username == dic["username"]:

                            md5_obj = hashlib.md5(sor)

                            md5_obj.update(dic['password'].encode('utf-8'))

                           pawd = md5_obj.hexdigest()

                           if pawd-txt == pwd:

                                    dic['status'] = True

                           else:

                                  dic['reason'] = ‘密码错误’

                           break

               else:

                        '用户不存在'

                    dic['reason']='用户不存在'

                     

        else:                            # '''已经登录成功''' 

             

server = socketserver.TCPServer(('127.0.0.1',8080),Mysocket)

server.server_forever()

client

import socket

import hashlib

import json

sk = socket.socket()

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

dic ={'status':False,'username':None,'password':None}

c = 3

while c:

    username = input('请输入用户名')

    password = input('请输入密码')

    md5_obj = hashlib.md5(password.encode('utf-8'))

    mf5_obj.update(username.encode('utf-8'))

    pawd_m=md5_obj.hexdigest()

   dic['username'] = username

   dic['passwoed'] = pawd_m

    str_dic = json.dumps(dic)

    sk.send(str_dic.encode('utf-8'))

 

 

 

sk.close()

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值