zoukankan      html  css  js  c++  java
  • Python Day 26 网络编程 ( 网络编程基础 socket tcp)

     Python Day 26 网络编程 ( 网络编程基础 socket tcp )

    笔记:

    https://mubu.com/doc/TMoGNU0xW

    楔子: 同一电脑中两个py文件如何交互

    软件编程的架构:

      C/S 架构  (client/server)

      B/S 架构 (broser/server) 

    网络基础

      一个程序如何在网络上找到另一个程序?

        ip+端口

    ip地址:
      是一个四位点分十进制的一串数字

    子网掩码:
      是计算机根据你的ip地址生产的一个东西

    网段:
      ip地址 &(与) 子网掩码

    端口号:
         ip + prot 唯一确定一台主机上的某一个程序
    允许开发人员使用的端口号范围从8000开始 8000-10000

     

      osi七层模型

     

    socket概念

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

    套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。 
    
    基于文件类型的套接字家族
    套接字家族的名字:AF_UNIX
    
    unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
    
    基于网络类型的套接字家族
    套接字家族的名字:AF_INET
    
    (还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
    套接字发展史

    tcp协议和udp协议

    TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。

    1)第一次握手:
    Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
    
    (2)第二次握手:
    Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
    
    (3)第三次握手:
    Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
    
    SYN攻击:
    
    在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
        netstat -nap | grep SYN_RECV
    三次握手
    所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发
    
     
    由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭
    
    第一次挥手:
    Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
    第二次挥手:
    Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
    第三次挥手:
    Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
    第四次挥手:
    Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
    四次挥手

    UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。

    套接字(socket)初使用

       基于TCP协议的socket ,tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端

    Socket 对象(内建)方法

    函数描述
    服务器端套接字
    s.bind() 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
    s.listen() 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
    s.accept() 被动接受TCP客户端连接,(阻塞式)等待连接的到来
    客户端套接字
    s.connect() 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
    s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    公共用途的套接字函数
    s.recv() 接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
    s.send() 发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
    s.sendall() 完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
    s.recvfrom() 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
    s.sendto() 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
    s.close() 关闭套接字
    s.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
    s.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port)
    s.setsockopt(level,optname,value) 设置给定套接字选项的值。
    s.getsockopt(level,optname[.buflen]) 返回套接字选项的值。
    s.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
    s.gettimeout() 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
    s.fileno() 返回套接字的文件描述符。
    s.setblocking(flag) 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
    s.makefile() 创建一个与该套接字相关连的文件

    sk = socket.socket(type=socket.SOCK_STREAM) #默认tcp

     复用端口

    import socket
    from socket import SOL_SOCKET,SO_REUSEADDR
    sk = socket.socket()
    sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    sk.bind(('127.0.0.1', 8091))
    sk.listen()

    一次建立连接发送消息

    import socket
    sk_server = socket.socket()
    sk_server.bind(('127.0.0.1',8090)) #绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
    sk_server.listen(5) #开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
    
    sock,addr = sk_server.accept()  #被动接受TCP客户端连接,(阻塞式)等待连接的到来
    msg_r = sock.recv(1024).decode('utf-8') #接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
    print(msg_r)
    sock.send('收到了:{}'.format(msg_r).encode('utf-8')) #send只能发送byte
    sock.close()  #关闭套接字
    sk_server.close() #关闭套接字
    单次 Server
    import socket
    sk_server = socket.socket()
    sk_server.connect(('127.0.0.1',8090))
    msg_s = input('>>>')
    sk_server.send(msg_s.encode('utf-8'))
    sk_server.close()
    单次 Client

    单客户端重复发送消息:

    #多次server,可触发关闭
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8091))
    sk.listen(5)
    sock,addr = sk.accept()
    while True:
        msg_r = sock.recv(1024).decode('utf-8')
        sock.send('收到了:{}'.format(msg_r).encode('utf-8'))
        if msg_r.upper() == 'Q':
            break
        print(msg_r)
        # msg_s = input('>>>')
        # sock.send(msg_s.encode('utf-8'))
        # if msg_s.upper() == 'Q':
        #     break
    重复接受 Server
    #多次client,带关闭
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8091))
    while True:
        msg_s = input('>>>')
        sk.send(msg_s.encode('utf-8'))
        if msg_s.upper() == 'Q':
            break
        msg_r = sk.recv(1024).decode('utf-8')
        if msg_r.upper() == 'Q':
            break
        print(msg_r)
    重复发送 Client

    多客户端重复发送消息:

    普通版
    import socket
    import datetime
    sk = socket.socket()
    sk.bind(('192.168.12.59', 8091))
    sk.listen(5)
    
    while True:
        conn, addr = sk.accept()
    
        msg_init_s = 'hello!蓉蓉在线,你叫什么?'
        conn.send(msg_init_s.encode('utf-8'))
    
        while True:
            msg_r = conn.recv(1024).decode('utf-8')
            if msg_r.upper() == 'Q':
                break
            print(msg_r)
            msg_s = input('>>>')
            msg_s_format = '33[31m{}:33[0m
    33[32m{}33[0m'.format(datetime.datetime.now(),msg_s)
            conn.send(msg_s_format.encode('utf-8'))
            # print(msg_s_format)
            # conn.send(msg_r.encode('utf-8'))
        conn.close()
    sk.close()
    Server
    import socket
    import datetime
    sk = socket.socket()
    sk.connect(('192.168.12.59', 8091))
    
    msg_init_r = sk.recv(1024).decode('utf-8')
    print(msg_init_r)
    
    while True:
        msg_s = input('>>>')
        msg_s_format = '33[31m{}:33[0m
    33[32m{}33[0m'.format(datetime.datetime.now(),msg_s)
        sk.send(msg_s_format.encode('utf-8'))
        if msg_s.upper() == 'Q':
            break
        msg_r = sk.recv(1024).decode('utf-8')
        if msg_r.upper() == 'Q':
            break
        print(msg_r)
    sk.close()
    Client
    多线程:
    import socket
    from socket import SOL_SOCKET, SO_REUSEADDR
    import threading
    import datetime
    
    def tcplink(fd, addr):
        while True:
            print(addr)
            msg_r = fd.recv(1024).decode('utf-8')
            if msg_r.upper() == 'Q':
                break
            msg_s = f'{datetime.datetime.now()} ,收到了{msg_r}'
            print(msg_s)
            fd.send(msg_s.encode('utf-8'))
            if msg_s.upper() == 'Q':
                break
        fd.close()
    
    
    sk = socket.socket()
    sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    sk.bind(('192.168.12.59', 8091))
    sk.listen(5)
    
    while True:
        fd, addr = sk.accept()
        t = threading.Thread(target=tcplink, args=(fd, addr))
        t.start()
    
    sk.close()
    支持多客户端的 Server
    import socket
    
    sk = socket.socket()
    # sk.connect(('192.168.12.84',8091))
    sk.connect(('192.168.12.59',8091))
    
    while True:
        msg_s = input('请输入消息:')
        sk.send(msg_s.encode())
        if msg_s.upper() == 'Q':
            break
    
        msg_r = sk.recv(1024).decode('utf-8')
        if msg_r.upper() == 'Q':
            break
        print(msg_r)
    
    sk.close()
    Client

    思考:

      1、你知道哪些研发相关的架构?
          b/s c/s
    2、arp协议做了什么事情?
        通过ip地址找mac
    3、交换机的通讯方式?
        广播单播组播
    4、你认为tcp和udp的区别?
        tcp
          安全可靠 面向连接(3次握手) 字节流形式传输
        udp
          快
    5、请描述出osi五层模型?
        应用层 传输层 网络层 数据链路层 物理层
    1、网段ip怎么确定?
         二进制ip和二进制子网进行与
    2、请描述B/S架构的优势
          
  • 相关阅读:
    Chapter 03Using SingleRow Functions to Customize Output(03)
    Chapter 03Using SingleRow Functions to Customize Output(01)
    Chapter 04Using Conversion Functions and Conditional ExpressionsNesting Functions
    Chapter 04Using Conversion Functions and Conditional ExpressionsGeneral Functions
    Chapter 11Creating Other Schema Objects Index
    传奇程序员John Carmack 访谈实录 (zz.is2120)
    保持简单纪念丹尼斯里奇(Dennis Ritchie) (zz.is2120.BG57IV3)
    王江民:传奇一生 (zz.is2120)
    2011台湾游日月潭
    2011台湾游星云大师的佛光寺
  • 原文地址:https://www.cnblogs.com/eailoo/p/9135047.html
Copyright © 2011-2022 走看看