zoukankan      html  css  js  c++  java
  • Python 学习笔记(七)socket

    1.网络七层模型及主要协议

    2.TCP的“三次握手”和四次挥手

    三次握手

    Step1:首先客户端向服务器端发送一段TCP报文;

    Step 2:服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段,并返回一段TCP报文;

    Step 3:客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段,并返回最后一段TCP报文。

    此后客户端和服务器端进行正常的数据传输。

    四次挥手

    Step 1:首先客户端想要释放连接,向服务器端发送一段TCP报文;

    Step 2:服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,随后服务器端结束ESTABLISHED阶段,进入CLOSE-WAIT阶段(半关闭状态)并返回一段TCP报文;

    Step 3:服务器端自从发出ACK确认报文之后,经过CLOSED-WAIT阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文;

    Step 4:客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文。

    服务器端收到从客户端发出的TCP报文之后结束LAST-ACK阶段,进入CLOSED阶段。由此正式确认关闭服务器端到客户端方向上的连接。

    3.socket

    socket(简称 套接字)是进程间通信的一种方式,它与其它进程间通信的一个主要不同是:socket 可以实现不同机器间的进程通信。

    下面是简单的Case,帮助理解。

     客户端

    from socket import socket, AF_INET, SOCK_STREAM
    
    ##表示创建一个客户端的socket
    
    client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP;
    
    ##定义一个连接的目标
    
    con_address = ('IP地址',端口号)
    
    ##告诉客户端要连接的服务器的地址和端口号
    
    client.connect(con_address)
    
    ##发送data
    
    client.send('python学习笔记'.encode('utf-8'))
    
    ##关闭
    socket.close()

     服务端,创建socket服务器

    from socket import socket, AF_INET, SOCK_STREAM
    ##创建一个socket对象
    server = socket(AF_INET, SOCK_STREAM)
    
    ##绑定端口号
    server.bind('',端口号) ##第一个参数为空字符串时,表示本机的IP
    
    ##开启监听状态
    server.listen(5)  ###参数为整型,表示消息可堆积的数量。
    
    while true:
      socket, addr_info = server.accept() ##阻塞的,表示没有连接的时候,一直等待。返回值为socket 和 addr_info。wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For IP sockets ,the address info is a pair (hostaddr,port).
      
      ##打印下
      print(socket, addr_info)
      
      ##读取接受到的信息
      recv_data = socket.recv(512).decode('utf-8')  ##512是我们定义的bufsize.这个方法返回的是bytes。
      print('{}发送过来的消息是:{}' .format(addr_info[0] , recv_data))
    
      ##关闭
      socket.close()

    4.客户端+服务器 交互通信(单信息交叉)

    客户端

    from socket import socket, AF_INET, SOCK_STREAM
    
    ##表示创建一个客户端的socket
    
    client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP;
    
    ##定义一个连接的目标
    
    con_address = ('IP地址',端口号)
    
    ##告诉客户端要连接的服务器的地址和端口号
    
    client.connect(con_address)
    while True:
      msg = input('客户端输入:')
      client.send(msg.encode('utf-8'))
      if msg == 'byebye':
        break
      ##接受服务器端的msg
      recv_data = socket.recv(512).decode('utf-8')  ##512是我们定义的bufsize.这个方法返回的是bytes。
      print('服务器端发送过来的消息是:{}' .format(recv_data))
      if recv_data == 'byebye':
        break
    ##关闭
    socket.close()

    服务端

    from socket import socket, AF_INET, SOCK_STREAM
    ##创建一个socket对象
    server = socket(AF_INET, SOCK_STREAM)
    
    ##绑定端口号
    server.bind('',端口号) ##第一个参数为空字符串时,表示本机的IP
    
    ##开启监听状态
    server.listen(5)  ###参数为整型,表示消息可堆积的数量。
    
    while True:
      socket, addr_info = server.accept() ##阻塞的,
      while True: ###保证可以对多个客户端,不能因为一个客户端,关闭所有。
        ##读取接受到的信息
        recv_data = socket.recv(512).decode('utf-8')  
        print('客户端发送过来的消息是:{}' .format(recv_data))
        if recv_data == 'byebye':  ##如果客户端说byebye,就退出
          break
        msg = input('服务器端输入:')
        socket.send(msg.encode('utf-8'))
        if msg == 'byebye' : ##如果我们说了byebye,退出
          break  
      ##关闭
      socket.close()
      print(addr_info,'离开了!')

    5.借助线程,一方可以发送多个消息

    即客户端与服务端之间的通信不必要限制为一来一回,一问一答

    服务端

    ##服务器端,创建socket服务器
    
    from socket import socket, AF_INET, SOCK_STREAM
    from threading import Thread
    ##创建一个socket对象
    server = socket(AF_INET, SOCK_STREAM)
    
    ##绑定端口号
    server.bind('',端口号)##第一个参数为空字符串时,表示本机的IP
    
    ##开启监听状态
    server.listen(5)  ###参数为整型,表示消息可堆积的数量
    
    ##任务
    def send_msg(socket)
      while True:
        msg = input('输入要发送的消息:')
        socket.send(msg.encode('utf-8'))
    
    def recv_msg(socket)
      while Ture:##可以持续收消息
        data=socket.recv(512).decode('utf-8')
        if len(data)==0:
          break
        print('收到客户端的消息是',data)
    
    while True:
      socket, addr_info = server.accept() ##阻塞的
      t_send = Thread(target=send_msg, args=(socket,))
      t_recv = Thread(target=recv_msg, args=(socket,))
      t_send.start()
      t_recv.start()

    客户端

    from socket import socket, AF_INET, SOCK_STREAM
    from threading import Thread
    ##表示创建一个客户端的socket
    
    client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP;
    
    ##定义一个连接的目标
    
    con_address = ('IP地址',端口号)
    
    ##告诉客户端要连接的服务器的地址和端口号
    
    client.connect(con_address)
    
    
    ##任务
    def send_msg(socket)
      while True:
        msg = input('输入要发送的消息:')
        socket.send(msg.encode('utf-8'))
    
    def recv_msg(socket)
      while Ture:##可以持续收消息
        data=socket.recv(512).decode('utf-8')
        if len(data)==0:
          break
        print('收到服务器端的消息是',data)
    
    
    t_send = Thread(target=send_msg, args=(client,))
    t_recv = Thread(target=recv_msg, args=(client,))
    t_send.start()
    t_recv.start()

     6.web客户端访问

    不写专门的客户端,借助web进行访问,并且支持多个web同时访问(通过协程实现)。

    web Server 的代码(服务端)

    '''
    cilent:浏览器客户端
    浏览器发出请求(request),Server端返回响应(response)。
    request 包含:request 行(里面有请求方法--get,协议--HTTP/1.1)、请求头【键值对】、请求体(POST请求时的数据)。
    response 包含:response 行(里面有协议--HTTP/1.1,状态--例如200 ok)、响应头【键值对】、响应体(数据)。
    '''
    
    
    ##服务器端,创建socket服务器
    import gevent   ##导入协程的包
    import gevent import monkey
    ##注意此时的socket 一定要来自gevent 的包,进行了继承和封装
    ##from socket import socket, AF_INET, SOCK_STREAM
    from gevent import socket
    from threading import Thread
    
    monkey.patch_all()  
    
    ##创建一个socket对象
    server = socket.socket()
    
    ##绑定端口号
    server.bind('',端口号)---第一个参数为空字符串时,表示本机的IP
    
    ##开启监听状态
    server.listen(5)  ###参数为整型,表示消息可堆积的数量
    
    ##处理客户端的访问
    def handle_client(socket)
      recv_data = socket.recv(512).decode('utf-8')
      print(recv_data)
    
      ##每次访问返回的信息是
      
      msg ='欢迎来此访问!'
      ##格式化response,需要有响应行、响应头
      resp_line = 'HTTP/1.1 200 OK\r\n' ##\r\n格式有换行要求
      resp_header = 'Content-Type:text/html\r\ncharset=utf-8\r\nServer:testServer\r\n' 
    
      resp=  resp_line + resp_header +'\r\n'+msg  ##拼凑完整的返回体。注意返回体结束结尾需是两个换行,添加一个
    
      socket.send(resp.encode('utf-8'))
      socket.colse() ##聊天结束
    
    while True:
      socket, addr_info = server.accept()  
      print(addr_info,'请求访问!')
      gevent.spawn(handle_cilent,socket)

     7.socket 常用函数和方法的梳理

    类型 函数或方法 解释

    服务端套接字函数

    s.bind()
    绑定(主机,端口号)到套接字
    s.listen()
    开始TCP监听
    s.accept()
    被动接受TCP客户的连接,(阻塞式)等待连接的到来
    客户端套接字函数 s.connect() 主动初始化TCP服务器连接
    s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    公共用途的套接字函数
       
    s.recv( 收TCP数据
    s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    s.sendall()

    发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)

    s.recvfrom() 接收UDP数据
    s.sendto() 发送UDP数据
    s.getpeername() 连接到当前套接字的远端的地址
    s.getsockname() 当前套接字的地址
    s.getsockopt() 返回指定套接字的参数
    s.setsockopt() 设置指定套接字的参数
    s.close() 关闭套接字
    面向锁的套接字方法 s.setblocking() 设置套接字的阻塞与非阻塞模式
    s.settimeout() 置阻塞套接字操作的超时时间
    s.gettimeout() 获取阻塞套接字操作的超时时间
    面向文件的套接字方法 s.fileno() 套接字的文件描述符
    s.makefile() 创建一个与该套接字相关的文件

    参考

    1.TCP/IP协议(一)网络基础知识 网络七层协议

    https://www.cnblogs.com/wanghuaijun/p/10092930.html

    2.计算机各层网络协议 

    https://www.cnblogs.com/weiliuyby/p/8030175.html

    3.Python3之socket编程

    https://www.cnblogs.com/zhangyingai/p/7097922.html

    4.详解 TCP 连接的“ 三次握手 ”与“ 四次挥手 ”

    https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc

  • 相关阅读:
    js数字格式化(加千分位逗号)
    [css]解决iframe在ios设备上无法滚动
    判断当前是否在微信浏览器环境
    TortoiseGit 提交代码每次需要输入用户名和密码?
    如何编写jQuery插件
    函数作用域
    HTTP动词
    如何减少全局变量污染?
    mysql表大字段最大长度限制设置
    update left join 多表关联更新
  • 原文地址:https://www.cnblogs.com/xuliuzai/p/15506254.html
Copyright © 2011-2022 走看看