zoukankan      html  css  js  c++  java
  • Day 36 网络编程总结

    一. 软件开发的架构

    c/s架构 client-server 用户层面的架构,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大,现在不流行了

       几乎包括了所有网络开发的架构形态

    b/s架构 browser - server 也是c/s架构,大势所趋

       其实也是一种client客户端,不需要安装应用程序,可以在网页上操作,比较方便,统一了所有应用的入口

    二. 网络基础

    ip地址和ip协议:

    规定网络地址的协议就叫做ip协议,32位2进制表示,范围0.0.0.0-255.255.255.255

    IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个是确定哪些地址在同一个子网络。

    mac地址:网卡

    以太网规定接入internet的设备都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址,是唯一的,通常由12位16进制数表示

    # mac地址唯一的,为什么要有ip地址?
    # 192.168.11.***
    # 256台 0-255
    # 192.168.***.***
    # 256^2
    # 192.***.***.***
    # 256^3

    arp协议:查询ip地址和mac地址的对应关系

    交换机:同一个局域网内的机器之间的交流

                局域网与交换机

                  广域网与路由器

    路由器:跨局域网机器之间的交流。 是连接因特网中各局域网,广域网的设备,又称网关设备是用于连接多个逻辑上分开的网络,网络层的互联设备。

    局域网:是指在某一局域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网是封闭性的,可以由办公室内的两台计算器组成,也可以由公司内的上千台计算器组成。

    子网掩码:就是表示子网络特征的一个参数。形式上等同于ip地址,也是一个32位二进制数字,网络部分全部为1,主机部分全部为0.

          比如,IP地址是172.16.10.1,如果已知网络部分是前24位,主机部分是后8位,那么子网掩码就是11111111,11111111,11111111,00000000,

          写成十进制就是255.255.255.0

          知道”子网掩码”,我们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算

          (两个数位都为1,运算结果为1,否则为0),然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否则就不是。 

    192.168.12.62
    11000000.10101000.00001011.00111110
    11111111.11111111.11111111.00000000
    11000000.10101000.00001011.00000000   == 192.168.0.0
    255.255.0.0
    
    192.168.11.94
    255.255.0.0
    11000000.10101000.00001011.01011110
    11111111.11111111.11111111.00000000  == 192.168.0.0

    网管ip:跨局域网的机器之间不能直接通信,只能通过网管ip通信

    TCP协议和UDP协议

    端口:

    通过ip地址+端口来区分不同的服务的

    TCP协议:三次握手,四次挥手

    当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方“握手”之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex) 的通信。

      这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。

    UDP协议:

    当应用程序希望通过UDP与一个应用程序通信时,传输数据之前源端和终端不建立连接。

      当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。

    tcp和udp的对比:

    tcp-传输控制协议

    相当于打电话:独占一个通道,在断开连接钱不能建立另一个连接,即两人在通话过程中第三方不能打入电话

    是面向连接的,可靠的字节流服务,

    当客户与服务器彼此交换数据前,必须先在双方之间建立一个tcp连接,之后才能传输数据。

    tcp提供超时重发,丢弃重复数据,检验数据,流量控制的功能,保证数据已定从一端传到另一端

    这种优化机制,容易出现黏包现象

    udp-用户数据包协议 相当于写信

    是一个简单的面相数据包的运输层协议

    udp不提供可靠性,由于udp在传输数据包前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,所以传输速度很快,所以不会出现黏包现象

    互联网协议与osi模型

    每层运行常见物理设备

    每层运行常见的协议

    socket概念-网络编程和python相关

    Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

    基于tcp协议的socket

    tcp是基于连接的,必须先启动服务器,然后再启动客户端去连接服务器

    server端

    import socket
    from socket import SOL_SOCKET,SO_REUSEADDR #加入一条socket配置,重用ip和端口
    sk= socket.socket()
    sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #在bind前加
    sk.bind(('127.0.0.1',8000)) #把地址绑定到套接字
    sk.listen() #监听链接
    
    conn,addr = sk.accept() #接收客户端链接
    ret = conn.recv(1024) # 接收客户端信息
    print(ret) #打印客户端信息
    conn.send(b'hi') #向客户端发送信息
    conn.close() #关闭客户端套接字
    sk.close() #关闭服务器套接字(可选)

    client端

    import socket
    sk = socket.socket() #创建客户套接字
    sk.connect(('127.0.0.1',8000)) #尝试连接服务器
    sk.send(b'hello')
    ret = sk.recv(1024) #对话(发送/接收)
    print(ret)
    sk.close() #关闭客户套接字

    udp是无连接的,启动服务之后可以直接接受消息,不需要提前建立连接

    server端

    import socket
    udp_sk = socket.socket(type=socket.SOCK_DGRAM) # 创建一个服务器的套接字
    udp_sk.bind(('127.0.0.1',9000)) # 绑定服务器套接字
    msg,addr = udp_sk.recvfrom(1024)
    print(msg)
    udp_sk.sendto(b'hi',addr) # 对话(接收与发送)
    udp_sk.close() # 关闭服务器套接字

    client端

    import socket
    ip_port = ('127.0.0.1',9000)
    udp_sk = socket.socket(type=socket.SOCK_DGRAM)
    udp_sk.sendto(b'hello',ip_port)
    back_msg,addr = udp_sk.recvfrom(1024)
    print(back_msg.decode('utf-8'),addr)

    qq聊天

    server端

    import socket
    ip_port = ('127.0.0.1',8888)
    udp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    udp_server_socket.bind(ip_port)
    
    while True:
        qq_msg,addr = udp_server_socket.recvfrom(1024)
        print('来自[%s:%s]的一条消息:33[1;44m%s33[0m'%(addr[0],addr[1],qq_msg.decode('utf-8')))
        reply_msg = input('请回复:').strip().encode('utf-8')
    
        udp_server_socket.sendto(reply_msg,addr)

    client端

    import socket
    
    BUFSIZE = 1024
    
    udp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    qq_name_dic = {'taibai':('127.0.0.1',8888),
                   'nero':('127.0.0.1',8888),
                   'egg':('127.0.0.1',8888),
                   'yuan':('127.0.0.1',8888)}
    
    while True:
        qq_name = input('请输入聊天对象:').strip()
        while True:
            msg = input('请输入消息:q退出')
            if msg =='q':break
            if not msg or not qq_name or qq_name not in qq_name_dic:continue
            udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
    
            reply_msg,addr = udp_client_socket.recvfrom(BUFSIZE)
    
            print('来自[%s:%s]的一条消息:33[1;44m%s33[0m' %(addr[0],addr[1],reply_msg.decode('utf-8')))
    
    udp_client_socket.close()

    黏包

    res=subprocess.Popen(cmd.decode('utf-8'),
    shell=True,
    stderr=subprocess.PIPE,
    stdout=subprocess.PIPE)
    
    的结果的编码是以当前所在的系统为准的,如果是windows,那么res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码
    
    且只能从管道里读一次结果

    同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是黏包。

    注意:只有TCP有粘包现象,UDP永远不会粘包

    tcp协议的拆包机制

    当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。 
    MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。

    即面向流的通信是无消息保护边界的。
    可靠黏包的tcp协议:tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。

    发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

    UDP不会发生黏包:

    udp不会使用块的合并优化算法,面对消息的童心是有消息保护边界的。

    发生黏包的两种情况:

    情况一:发送方的缓存机制

    发送端需要等缓冲区满才发送出去,造成黏包(发送时间间隔很短,数据很小,会何在一起,产生黏包

    服务端

    from socket import *
    ip_port=('127.0.0.1',8080)
    
    tcp_socket_server=socket(AF_INET,SOCK_STREAM)
    tcp_socket_server.bind(ip_port)
    tcp_socket_server.listen(5)
    
    
    conn,addr=tcp_socket_server.accept()
    
    
    data1=conn.recv(10)
    data2=conn.recv(10)
    
    print('----->',data1.decode('utf-8'))
    print('----->',data2.decode('utf-8'))
    
    conn.close()

    客户端

    import socket
    BUFSIZE=1024
    ip_port=('127.0.0.1',8080)
    
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    res=s.connect_ex(ip_port)
    
    
    s.send('hello'.encode('utf-8'))
    s.send('egg'.encode('utf-8'))

    结果

    -----> helloegg
    ----->

    情况二:接收方的缓存机制 

    接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

    服务端

    from socket import *
    ip_port=('127.0.0.1',8080)
    
    tcp_socket_server=socket(AF_INET,SOCK_STREAM)
    tcp_socket_server.bind(ip_port)
    tcp_socket_server.listen(5)
    
    
    conn,addr=tcp_socket_server.accept()
    
    
    data1=conn.recv(2) #一次没有收完整
    data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的
    
    print('----->',data1.decode('utf-8'))
    print('----->',data2.decode('utf-8'))
    
    conn.close()

    客户端

    import socket
    BUFSIZE=1024
    ip_port=('127.0.0.1',8080)
    
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    res=s.connect_ex(ip_port)
    
    
    s.send('hello egg'.encode('utf-8'))

    结果:

    -----> he
    -----> llo egg

    黏包现象只发生在tcp协议中:

    1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

    2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

    黏包的解决方案

    解决方案进阶 struct模块

    server端

    import os,json,struct,socket
    
    filepath = r'/Users/lixinwei/PycharmProjects/untitled1/xxglxt'
    filename = os.path.basename(filepath)
    filesize = os.path.getsize(filepath)
    
    sk=socket.socket()
    sk.bind(('127.0.0.1',8300))
    sk.listen()
    
    conn,addr = sk.accept()
    print('客户端',addr)
    dic = {'filename':filename,
           'filesize':filesize}
    
    str_dic = json.dumps(dic).encode('utf-8')
    dic_len = len(str_dic)
    length = struct.pack('i',dic_len)
    
    conn.send(length)
    conn.send(str_dic)
    
    with open(filename,'rb') as f:
           while filesize:
                  content = f.read(4096)
                  conn.send(content)
                  filesize -= len(content)
    
    conn.close()
    sk.close()

    client端

    import json,socket,struct
    
    sk = socket.socket()
    sk.connect(('127.0.0.1',8300))
    
    dic_len = sk.recv(4)
    dic_len = struct.unpack('i',dic_len)[0]  # 字典真正的长度50
    dic = sk.recv(dic_len)
    str_dic = dic.decode('utf-8')
    dic = json.loads(str_dic)
    
    with open(dic['filename'],'wb') as f:
        while dic['filesize']:
            content = sk.recv(4096)
            dic['filesize'] -= len(content)
            f.write(content)
    
    sk.close()

    七.验证客户端链接的合法性

  • 相关阅读:
    双网卡主机无法管理的故障
    hosts文件导致无法访问网站
    获取webshell的十种方法
    XSS跨站攻击
    Ubuntu 使用中的问题总结
    ubuntu linux 13.04更新
    mysql root密码重置
    防火墙工作模式简介
    SE 2014年4月30日
    SE 2014年4月29日
  • 原文地址:https://www.cnblogs.com/kateli/p/9011078.html
Copyright © 2011-2022 走看看