• socket套接字编程 HTTP协议


    socket套接字编程
     
    套接字介绍
     
    1. 套接字 : 实现网络编程进行数据传输的一种技术手段
     
    2. Python实现套接字编程:import  socket
     
    3. 套接字分类
    >流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案。(面向连接--tcp协议--可靠的--流式套接字)
     
    >数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案。(无连接--udp协议--不可靠--数据报套接字)
     

     


     tcp套接字编程
     
    服务端流程
     

     1 from socket import * 
     2 
     3 #创建流式套接字
     4 sockfd = socket(AF_INET,SOCK_STREAM,0)
     5 
     6 #绑定IP端口
     7 sockfd.bind(('127.0.0.1',8888))
     8 
     9 #设置监听套接字,创建监听队列
    10 sockfd.listen(5)
    11 
    12 while True:
    13     print("waiting for connect....")
    14     #等待客户端链接
    15     connfd,addr = sockfd.accept()
    16     print("connect from",addr)
    17 
    18     while True:
    19         #收发消息
    20     
    21         data = connfd.recv(1024)
    22 
    23         if not data:
    24             break   
    25         
    26         print(data.decode())
    27 
    28         #发消息
    29         connfd.send('来,确认下眼神'.encode())
    30     #关闭套接字
    31     connfd.close()
    32 
    33 sockfd.close()
    TCP_server.py


     
    1. 创建套接字
    sockfd=socket.socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0)
    功能:创建套接字
    参数:  socket_family  网络地址类型 AF_INET表示ipv4
        socket_type  套接字类型 SOCK_STREAM(流式)  SOCK_DGRAM(数据报)
        proto  通常为0  选择子协议
    返回值: 套接字对象

    2. 绑定地址
    >本地地址 : 'localhost' , '127.0.0.1'
    >网络地址 : '172.40.91.185'
    >自动获取地址: '0.0.0.0'
     

    sockfd.bind(addr)
    功能: 绑定本机网络地址
    参数: 二元元组 (ip,port)  ('0.0.0.0',8888)
    ```
     
    3. 设置监听
    sockfd.listen(n)
    功能 : 将套接字设置为监听套接字,确定监听队列大小
    参数 : 监听队列大小
    ```
    4. 等待处理客户端连接请求
    connfd,addr = sockfd.accept()
    功能: 阻塞等待处理客户端请求
    返回值: connfd  客户端连接套接字
             addr  连接的客户端地址
    ```
    5. 消息收发
    data = connfd.recv(buffersize)
    功能 : 接受客户端消息
    参数 :每次最多接收消息的大小
    返回值: 接收到的内容
     
    n = connfd.send(data)
    功能 : 发送消息
    参数 :要发送的内容  bytes格式
    返回值: 发送的字节数
    ```
                
    6. 关闭套接字 
    sockfd.close()
    功能:关闭套接字
    ```
     客户端流程  

     1 # 接受的客户端
     2 
     3 
     4 from socket import * 
     5 
     6 #创建套接字
     7 sockfd = socket(AF_INET,SOCK_STREAM)
     8 
     9 #发起连接
    10 sockfd.connect(('127.0.0.1',8888))
    11 
    12 while True:
    13     msg = input("发消息>>  ")
    14     #发送消息
    15     sockfd.send(msg.encode())
    16     if not msg:
    17         break
    18 
    19     #接收消息
    20     data = sockfd.recv(1024)
    21     print(data.decode())
    22 
    23 # msg = input("Msg>>")
    24 # scorfd.send(msg.encode())
    25 
    26 # data = sockfd.recv(1024)
    27 # print(data.decode())
    28 
    29 #关闭
    30 sockfd.close()
    TCP_client.py



               
    1. 创建套接字
    >注意:只有相同类型的套接字才能进行通信
                
    2. 请求连接

    sockfd.connect(server_addr)
    功能:连接服务器
    参数:元组  服务器地址

    3. 收发消息
    >注意: 防止两端都阻塞,recv send要配合
     
    4. 关闭套接字
     
     
    tcp 套接字数据传输特点
     
    >* tcp连接中当一端退出,另一端如果阻塞在recv,此时recv会立即返回一个空字串。
     
    >* tcp连接中如果一端已经不存在,仍然试图通过send发送则会产生BrokenPipeError
     
    >* 一个监听套接字可以同时连接多个客户端,也能够重复被连接
     
    网络收发缓冲区
     
    1. 网络缓冲区有效的协调了消息的收发速度
    2. send和recv实际是向缓冲区发送接收消息,当缓冲区不为空recv就不会阻塞。
        
     tcp粘包
     
     socket.time.sleep(0.1)
    >原因:tcp以字节流方式传输,没有消息边界。多次发送的消息被一次接收,此时就会形成粘包。
     
    >影响:如果每次发送内容是一个独立的含义,需要接收端独立解析此时粘包会有影响。
     
    >处理方法
    >>1. 人为的添加消息边界
    >>2. 控制发送速度
     
     
    UDP套接字编程
     
    服务端流程
     
     

     1 from socket import * 
     2 import sys 
     3 from time import ctime 
     4 
     5 
     6 ADDR = ('127.0.0.1',8888)
     7 BUFFERSIZE = 1024
     8 
     9 #创建数据报套接字
    10 sockfd = socket(AF_INET,SOCK_DGRAM)
    11 #绑定地址
    12 sockfd.bind(ADDR)
    13 
    14 #收发消息
    15 while True:
    16     data,addr = sockfd.recvfrom(BUFFERSIZE)
    17     print('recv from %s:%s'%(addr,data.decode()))
    18     sockfd.sendto
    19     (("[%s] 接收到消息"%ctime()).encode(),addr)
    20     
    21 sockfd.close()
    22 
    23 
    24 
    25 
    26 #服务端, udp
    27 from socket import * 
    28 
    29 HOST = '127.0.0.1'
    30 PORT = 9999
    31 ADDR = (HOST, PORT)
    32 #创建数据报套接字
    33 sockfd = socket(AF_INET,SOCK_DGRAM)
    34 #绑定地址
    35 sockfd.bind(ADDR)
    36 #收发消息
    37 while True:
    38     data,addr = sockfd.recvfrom(1024)
    39     print('Receive from %s:%s' % (addr,data.decode()))
    40     # sockfd.sendto(("[%s] 接收到消息"%ctime()).encode(),addr)
    41     sockfd.sendto("收到消息".encode(), addr)
    42 sockfd.close()
    UDP_server.py

        
    1. 创建数据报套接字
    sockfd = socket(AF_INET,SOCK_DGRAM)
    2. 绑定地址 
    sockfd.bind(addr)
    3. 消息收发        
    data,addr = sockfd.recvfrom(buffersize)
    功能: 接收UDP消息
    参数: 每次最多接收多少字节
    返回值: data  接收到的内容
        addr  消息发送方地址
     
    n = sockfd.sendto(data,addr)
    功能: 发送UDP消息
    参数: data  发送的内容 bytes格式
        addr  目标地址
    返回值:发送的字节数

    4. 关闭套接字
    sockfd.close()

    客户端流程
     

     1 from socket import * 
     2 import sys 
     3 
     4 #从命令行传入IP和端口
     5 #python3 udp_server.py 172.60.50.42 8888
     6 if len(sys.argv) < 3:
     7     print('''
     8            argv is error!!!
     9            input as 
    10            python3 udp_server.py 172.60.50.42 4260
    11            ''')
    12 
    13 HOST = sys.argv[1]
    14 PORT = int(sys.argv[2])
    15 ADDR = (HOST,PORT)
    16 BUFFERSIZE = 1024
    17 
    18 #创建数据报套接字
    19 sockfd = socket(AF_INET,SOCK_DGRAM)
    20 
    21 while True:
    22     data = input("消息>>")
    23     #直接回车退出
    24     if not data:
    25         break
    26     sockfd.sendto(data.encode(),ADDR)
    27     data,addr = sockfd.recvfrom(BUFFERSIZE)
    28     print('从服务器收到:',data.decode())
    29 sockfd.close()
    30 
    31 
    32 #客户端
    33 from socket import * 
    34 import sys 
    35 
    36 #命令行输入服务器地址
    37 if len(sys.argv) < 3:
    38     print('''argv is error !!
    39         start as
    40         python3 udp_client.py 127.0.0.1 8888
    41         ''')
    42     # raise
    43 
    44 HOST = sys.argv[1]
    45 PORT = int(sys.argv[2])#字符串转int类型
    46 ADDR = (HOST,PORT)
    47 
    48 #创建数据报套接字
    49 sockfd = socket(AF_INET, SOCK_DGRAM)
    50 
    51 while True:
    52     data = input("消息:   ")
    53     if not data:
    54         break
    55     sockfd.sendto(data.encode(),ADDR)
    56     data,addr = sockfd.recvfrom(1024)
    57     print("从服务端收到: ", data.decode())
    58 
    59 sockfd.close()
    60 
    61 # $ python3 udp_client.py 127.0.0.1 8888
    62 #要加ip名和端口名才能运行
    UDP_client.py


    1. 创建套接字
    2. 收发消息
    3. 关闭套接字
    ---------------
    >总结 :tcp套接字和udp套接字编程区别
    >>1. 流式套接字是以字节流方式传输数据,数据报套接字以数据报形式传输
    >>2. tcp套接字会有粘包,udp套接字有消息边界不会粘包
    >>3. tcp套接字保证消息的完整性,udp套接字则不能
    >>4. tcp套接字依赖listen accept建立连接才能收发消息,udp套接字则不需要
    >>5. tcp套接字使用send,recv收发消息,udp套接字使用sendto,recvfrom
    ---------------------  

    socket套接字属性 
    【1】 sockfd.type  套接字类型
     
    【2】 sockfd.family 套接字地址类型
     
    【3】 sockfd.getsockname() 获取套接字绑定地址
     
    【4】 sockfd.fileno() 获取套接字的文件描述符
     
    【5】 sockfd.getpeername() 获取连接套接字客户端地址
     
    【6】 sockfd.setsockopt(level,option,value)
            功能:设置套接字选项
            参数: level  选项类别   SOL_SOCKET
                option 具体选项内容
                value  选项值
     
    【7】 sockfd.getsockopt(level,option)  
            功能 : 获取套接字选项值
     


    UDP套接字广播

     1 #广播接收端   与udp的
     2 
     3 from socket import *
     4 
     5 HOST = ''
     6 PORT = 8880
     7 
     8 #创建数据报套接字
     9 s = socket(AF_INET,SOCK_DGRAM)#加默认参数0变成流式套接字SOCK_STREAM
    10 #固定接收端的端口号
    11 s.bind((HOST,PORT))
    12 #设置套接字可以接收广播, 有此后变成与udp不同
    13 s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)#broadcast
    14 #设置套接字保护端口号
    15 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#reuseaddr
    16 
    17 
    18 # while True:
    19 #     try:
    20 #         message,addr = s.recvfrom(1024)
    21 #         print("从{}获取信息{}:".
    22 #             format(addr,message.decode()))#decode将“字节流”按照某种规则转换成'文本'  str变unicode
    23 #         s.sendto(b"I am here",addr)
    24 #     except (KeyboardInterrupt,SyntaxError):#从终端ctrl+c的错
    25 #         raise
    26 #     except Exception as e:
    27 #         print(e)
    28 
    29 # s.close()
    30 
    31 while True:
    32     try:
    33         message,addr = s.recvfrom(1024)
    34         print("获取信息: {}".
    35             format(message.decode()))#decode将“字节流”按照某种规则转换成'文本'  str变unicode
    36         s.sendto(b"I am here",addr)
    37     except (KeyboardInterrupt,SyntaxError):#从终端ctrl+c的错
    38         raise#raise 引发一个异常
    39     except Exception as e:#异常类
    40         print(e)
    41 
    42 s.close()
    broadcast_recv.py
     1 # 发送端
     2 
     3 from socket import *
     4 from time import sleep 
     5 
     6 #发送广播的地址
     7 #<broadcast>
     8 dest = ('176.122.14.255',8880)#端口号9999一致
     9 #创建数据报套接字
    10 s = socket(AF_INET,SOCK_DGRAM)#dgram
    11 #设置能够发送广播
    12 s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)#broadcast
    13 
    14 while True:
    15     sleep(2)
    16     s.sendto("什么鬼啊aaaaaa啦啦啦啦啦啦啦".encode(),dest)#目标地址
    17     data,addr = s.recvfrom(1024)
    18     print("Received from %s:%s"%(addr,data.decode()))
    19 s.close()
    20 
    21 # while True:
    22 #     data = input('input: ')
    23 #     s.sendto(("@鸿182:"+ data).encode(),dest)
    24 # s.close()
    25 # 局域网聊天室,同端口号
    broadcast_send.py


    * 广播定义 : 一端发送多点接收
         
    * 广播地址 : 每个网络的最大地址为发送广播的地址,向该地址发送,则网段内所有主机都能接收。


     TCP套接字之HTTP传输
     HTTP协议 (超文本传输协议)
     
    1. 用途 : 网页获取,数据的传输
     
    2. 特点

    应用层协议,传输层使用tcp传输 

    简单,灵活,很多语言都有HTTP专门接口

    无状态,协议不记录传输内容

    http1.1 支持持久连接,丰富了请求类型
     
    3. 网页请求过程
      1.客户端(浏览器)通过tcp传输,发送http请求给服务端
      2.服务端接收到http请求后进行解析
      3.服务端处理请求内容,组织响应内容
      4.服务端将响应内容以http响应格式发送给浏览器
      5.浏览器接收到响应内容,解析展示

    HTTP请求(request)

      1 """
      2 httpserver v2.0
      3 env: python3.6
      4 io多路复用 和 http训练
      5 """
      6 
      7 from socket import *
      8 from select import *
      9 
     10 
     11 # 具体功能实现
     12 class HTTPServer:
     13     def __init__(self, host='0.0.0.0', port=8000, dir=None):
     14         self.host = host
     15         self.port = port
     16         self.dir = dir
     17         self.address = (host, port)
     18         # 多路复用列表
     19         self.rlist = []
     20         self.wlist = []
     21         self.xlist = []
     22         # 实例化对象时直接创建套接字
     23         self.create_socket()
     24         self.bind()
     25 
     26     # 创建套接字
     27     def create_socket(self):
     28         self.sockfd = socket()
     29         self.sockfd.setsockopt(SOL_SOCKET,
     30                                SO_REUSEADDR, 1)
     31 
     32     # 绑定地址
     33     def bind(self):
     34         self.sockfd.bind(self.address)
     35 
     36     # 启动服务
     37     def serve_forever(self):
     38         self.sockfd.listen(3)
     39         print("Listen the port %d" % self.port)
     40         # IO多路复用接收客户端请求
     41         self.rlist.append(self.sockfd)
     42         while True:
     43             rs, wx, xs = select(self.rlist,
     44                                 self.wlist,
     45                                 self.xlist)
     46             for r in rs:
     47                 if r is self.sockfd:
     48                     c, addr = r.accept()
     49                     self.rlist.append(c)
     50                 else:
     51                     # 处理请求
     52                     self.handle(r)
     53 
     54     def handle(self, connfd):
     55         # 接收HTTP请求
     56         request = connfd.recv(4096)
     57         # 客户端断开
     58         if not request:
     59             self.rlist.remove(connfd)
     60             connfd.close()
     61             return
     62         # 提取请求内容 (字节串按行分割)
     63         request_line = request.splitlines()[0]
     64         info = request_line.decode().split(' ')[1]
     65         print(connfd.getpeername(), ':', info)
     66 
     67         # 根据请求内容进行数据整理
     68         # 分为两类 1.请求网页  2.其他
     69         if info == '/' or info[-5:] == '.html':
     70             self.get_html(connfd, info)
     71         else:
     72             self.get_data(connfd, info)
     73 
     74     # 返回网页
     75     def get_html(self, connfd, info):
     76         if info == '/':
     77             # 请求主页
     78             filename = self.dir + "/index.html"
     79         else:
     80             filename = self.dir + info
     81         try:
     82             fd = open(filename)
     83         except Exception:
     84             # 网页不存在
     85             response = "HTTP/1.1 404 Not Found
    "
     86             response += 'Content-Type:text/html
    '
     87             response += '
    '
     88             response += '<h1>Sorry....</h1>'
     89         else:
     90             # 网页存在
     91             response = "HTTP/1.1 200 OK
    "
     92             response += 'Content-Type:text/html
    '
     93             response += '
    '
     94             response += fd.read()
     95         finally:
     96             # 将响应发送给浏览器
     97             connfd.send(response.encode())
     98 
     99     # 其他数据
    100     def get_data(self, connfd, info):
    101         response = "HTTP/1.1 200 OK
    "
    102         response += 'Content-Type:text/html
    '
    103         response += '
    '
    104         response += "<h1>Waiting for httpserver 3.0</h1>"
    105         connfd.send(response.encode())
    106 
    107 
    108 # 用户使用HTTPServer
    109 if __name__ == "__main__":
    110     """
    111     通过 HTTPServer类快速搭建服务,展示自己的网页
    112     """
    113     # 用户决定的参数
    114     HOST = '0.0.0.0'
    115     PORT = 8000
    116     DIR = './static'  # 网页存储位置
    117 
    118     httpd = HTTPServer(HOST, PORT, DIR)  # 实例化对象
    119     httpd.serve_forever()  # 启动服务
    HTTP_server.py
     1 #静态网页处理器
     2 #采用循环的模式,无法满足客户端长连接
     3 
     4 from socket import *#网络连接端点,导入模块
     5 
     6 #处理客户端请求, 交互
     7 def handleClient(connfd):#请求,调用函数
     8     request = connfd.recv(2048)#请求套接字接受文件
     9     # print('**************')
    10     # print(request)
    11     # print('**************')
    12     #按照行切割请求, 方便查看
    13     request_lines = request.splitlines()
    14     for line in request_lines:
    15         print(line.decode())#对分割后的文件解码
    16 
    17     try:
    18         f = open("index.html")
    19     except IOError:#找不到页面, 文件出错
    20         response = "HTTP/1.1 303 not Found
    "#http响应行
    21         response += '
    '#无响应头, 有空行
    22         response += '''====sorry ,file not find
    23                     *******************************
    24                     Sorry bing i love you miss you.
    25                     *******************************
    26                     '''
    27     else:#正常情况运行
    28         response = "HTTP/1.1 200 OK
    "#成功响应头, 响应体是页面
    29         response += '
    '
    30         response += f.read()#响应体是页面
    31         # for i in f:
    32         #     response += i
    33     finally:
    34         connfd.send(response.encode())#发送给浏览器
    35         connfd.close()
    36 
    37 #流程控制
    38 def main():
    39     sockfd = socket()#创建流式套接字
    40     sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#设置端口重用
    41     sockfd.bind(('0.0.0.0',8888))#绑定套接字
    42     sockfd.listen(10)#监听套接字
    43     while True:#循环监听等待连接
    44         print("Listen the port 8000..")
    45         connfd,addr = sockfd.accept()#创建另一个套接字连接请求,便于接收一个后再接收,
    46         #处理浏览器发来的请求
    47         handleClient(connfd)#跟客户顿交互
    48         connfd.close()#关闭套接字
    49      
    50 
    51 if __name__ == "__main__":#调用主函数
    52     main()
    53 
    54 
    55 # ---------------------------------------------------------------
    56 
    57 
    58 # #接收请求
    59 # #查看请求
    60 # #返回客户端请求
    61 
    62 # def handleClient():
    63 #     pass
    64 
    65 
    66 # #创建tcp套接字, 调用handleClient完成功能
    67 # def main():
    68 #     pass
    69 
    70 # if __name__=="__main__":
    71 #     main()
    HTTP_server.py
     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>欢迎进入Alan主页</title>
     6 </head>
     7 <body>
     8     <h1>Alan songzihong welcome to you </h1>
     9     <h1>what is you name</h1>
    10     <h1>How can i do cna help you </h1>
    11 </body>
    12 </html>
    13 
    14 <!-- 没保存会只显示文件名 -->
    15 <!-- html:5 + tab -->
    16 <!-- h1 + tab -->
    index.html


    * 请求行 : 具体的请求类别和请求内容
     
    ```
        GET         /        HTTP/1.1
        请求类别   请求内容     协议版本
    ```
     
    请求类别:每个请求类别表示要做不同的事情  
     
    ```        
            GET : 获取网络资源
            POST :提交一定的信息,得到反馈
            HEAD : 只获取网络资源的响应头
            PUT : 更新服务器资源
            DELETE : 删除服务器资源
            CONNECT
            TRACE : 测试
            OPTIONS : 获取服务器性能信息
    ```
     
    * 请求头:对请求的进一步解释和描述
    ```
    Accept-Encoding: gzip
    ```
    * 空行
    * 请求体: 请求参数或者提交内容


    http响应(response)
     
    1. 响应格式:响应行,响应头,空行,响应体
     
    * 响应行 : 反馈基本的响应情况
     
    ```         
    HTTP/1.1     200       OK
    版本信息    响应码   附加信息
    ```
     
    响应码 :  
    ```
    1xx  提示信息,表示请求被接收
    2xx  响应成功
    3xx  响应需要进一步操作,重定向
    4xx  客户端错误
    5xx  服务器错误
    ```
    * 响应头:对响应内容的描述
    ```             
    Content-Type: text/html
    ```
     
    * 响应体:响应的主体内容信息

  • 相关阅读:
    Linux安装Oracle 10g
    Linux安装Oracle 10g
    如何利用BI实现人力资源可视化管理
    mongdb修改密码
    如何利用BI实现人力资源可视化管理
    jquery获取浏览器和屏幕的高度和宽度
    perl概述
    'CheckLicensesAndNotify' has encountered a problem.
    Caused by: java.sql.SQLException: The MySQL server is running with the --read-only option so it cann
    Caused by: java.sql.SQLException: Value '0000-00-00 00:00:00' can not be represented as java.sql.Tim
  • 原文地址:https://www.cnblogs.com/Alan-Song/p/11181594.html
Copyright © 2020-2023  润新知