美文网首页
(1-2)socket -- sever

(1-2)socket -- sever

作者: 马梦里 | 来源:发表于2017-12-12 15:50 被阅读0次

    一、

    两层循环:一个是接收连接,一个是接收请求
    逻辑:

    1. 实例化、绑定服务器和端口、监听
    2. 无限循环,保持接收状态
    3. 接收到请求后,解析请求,拿到路径
    4. 根据路径找到对应的视图函数,视图函数会返回响应
    5. 发送响应,断开连接
    import socket
    
    s = socket.socket()
    host = '0.0.0.0'
    port = 3000
    
    s.bind((host, port))
    s.listen()
    
    while True:
        log('before accept')
        connection, address = s.accept()
        print(connection)
        print('address', address)
        print('after accept')
    
        request = b''
        buffer_size = 1024
        while True
            r = connection().rev(buffer_size)
            request += r 
            if len(r) < buffer_size:
                break
        print('ip and request, {}\n{}'.format(address, request.decode()))
        response = b'HTTP/1.1 233 VERY OK\r\nMJ:mamengli\r\n\r\n<h1>Hello World</h1>'
        connection().sendall(response)
        connection.close()
    
    1. 实例化socket
    2. 绑定ip和端口,并进行监听
      0.0.0.0表示任意ip都可以访问此服务器,访问的ip必须是3000
    3. 这是一个无限循环,表示一直接收客户端发过来的连接请求。
      accept()是一个阻断函数,当有客户端请求的时候,它会判断连接状态,然后往下走。如果没有请求连接,那么它会一直停留在这里。
      这就是socket的第一步:建立连接 图片.png
    4. 接收请求数据
      这又是一个无限循环,表示一直接收客户端发过来的数据(每次接收1024),直到接收完。
      这里request = b' ',如果把b 去掉,那么: 图片.png 表明客户端通过网络发送过来的数据是bytes类型,通过b‘ ’将其转换为str
      requestrequest.decode()的区别:
    类型转换.png
    1. 发送响应
      利用sendall()函数发送请求,这里的放松给网络传播的数据必须是bytes类型。发送完数据后,关闭连接。

    小知识点:

    1. 字符串的format()方法,实现字符串的格式化,使用方式:
      'name:{}, age:{}'.format('majun', 27)
      替代了%s %
    2. break:表示跳出当前循环(最底层的),还可以继续执 行外层循环,如果没有,那么循环结束。
      continue:跳出循环,继续执行循环。
    3. 数据类型的转换
      在客户端,数据在发送之前需要encode(),也就是将字符串转换为bytes类型。
      在服务器端,接收数据的格式也必须是bytes类型,不然格式不对称,所以有个b
      服务器端响应的时候,加上了一个b,表示发送的是字节类型。
      由此可知,encode()放和b的作用一样。
      由上图(类型转换.png)可以看到,request经过decode()后,变成了字符串类型。注意,第一个request前面有个b
      可见:
      encode()将字符串转换为bytes
      decode()bytes转换为str

    二、

    import socket
    
    def log(*args, **kwargs):
        print('log', *args, **kwargs)
    
    def route_index():
        header = 'HTTP/1.1 233 OK\r\nContent-Type: text/html\r\n'
        body = '<h1>Hello World</h1><img src="doge.gif"/>'
        r = '{}\r\n{}'.format(header, body)
        return r.encode()
    
    def html_content(path):
        with open(path, encoding='utf-8') as f:
            return f.read()
    
    def route_message():
        # 主页的处理函数, 返回主页的响应
        header = 'HTTP/1.1 233 OK\r\nContent-Type: text/html\r\n'
        body = html_content('html_basic.html')
        r = '{}\r\n{}'.format(header, body)
        return r.encode()
    
    def route_image():
        # 图片的处理函数, 读取图片并生成响应返回,二进制方式读取
        with open('doge.gif', 'rb') as f:
            header = b'HTTP/1.1 200 OK\r\nContent-Type: image/gif\r\n\r\n'
            // 拼接的时候,数据类型要一直
            image = header + f.read()
            return image
    
    def error(code=404):
        # 根据 code 返回不同的错误响应,目前只有 404
        e = {
            404: b'HTTP/1.1 404 NOT FOUND\r\nContent-Type: text/html\r\n\r\n<h1>NOT FOUND</h1>',
        }
        return e.get(code, b'')
    
    //  表驱动法
    def response_for_path(path):
        # 根据 path 调用相应的处理函数,没有处理的 path 会返回 404
        # 函数当做参数传递,不需要括号
        r = {
            '/': route_index,
            '/message': route_message,
            '/doge.gif': route_image,
        }
        response = r.get(path, error)
        # 调用函数的时候,需要加()
        return response()
    
    def run(host, port):
        #  with 可以保证程序中断的时候正确关闭 socket 释放占用的端口
        with socket.socket() as s:
            s.bind((host, port))
            s.listen()
            # server 用 connection 来接收请求和发送响应
            while True:
                # accept 为阻断函数
                connection, address = s.accept()
                # 这里只读取了 1024 字节的内容, 应该用一个循环全部读取
                request = connection.recv(1000)
                request = request.decode()
                # 因为 chrome 会发送空请求导致 split 得到空 list
                # 所以这里先判断一下 split 得到的数据的长度
                parts = request.split()
                log('parts', parts)
    
                if len(parts) > 0:
                    path = parts[1]
                    # 用 response_for_path 函数来得到 path 对应的响应内容
                    response = response_for_path(path)
                    # 把响应发送给客户端
                    connection.sendall(response)
                else:
                    log("接收到了一个空请求")
                connection.close()
    
    if __name__ == '__main__':
        config = dict(
            host='0.0.0.0',
            port=2000,
        )
        # 等价于:run(host = '0.0.0.0',port = 3000)
        run(**config)
    

    函数当做参数传递的时候不需要写括号和参数,有括号的时候表示 return 的结果

    1. GET / POST
      端口直接和host在一起;
      POST方法提交的数据储存在body中;
      GRT提交的数据在请求头;
    POST / HTTP/1.1
    Host: localhost:3000
    Connection: keep-alive
    
    author=gua&message=hello
    
    GET /?message=&author=gua HTTP/1.1
    Host: localhost:3000
    Connection: keep-alive
    

    2. with open
    读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。
    3. 读文件
    // 先打开文件,with自动调用close()方法,出错时,自动关闭
    // mode 为读文件的模式,r 表示读

    with open('/path/filename', 'mode') as f:
        // 接下来就是读文件,`read()`方法一次性读取所有文件
        // `python` 把文件读到内存,用一个`str`对象(字符串)表示
        f.read()
    

    如果文件很小,一次性读取很方便,如果文件很多,而且是配置文件的时候,就要用到readlines()方法了:

    for line in f.readlines()
        // `strip()` 方法可以去掉空格和行末的 `\r\n`
        print(line.strip())
    

    4. 写文件
    写文件和上面一样,只不过 r 改为 wf.read() 改为 f.write()
    rbwb 表示以二进制方法读写;
    open函数的其他参数:
    要读取非UTF-8编码的文件时,比如:gbk,传入参数 encoding = 'gbk',如果文件中有一些非法编码的字符:errors = 'ignore'可以解决;
    5. 异常处理:try except slse:
    try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。
    try语句将可能发生错误的代码包住,except用来捕获被包住代码可能发生的异常,如果异常匹配,则执行except里面的代码,如果没有异常,执行完try里面后的代码后,再执行else里面的代码;


    client 是用 s 来操作:s = socket.socket()
    s.connect((host, port)) s.send(request) s.recv(response)
    server 是用 connection 来操作
    connection, address = s.accept()
    connection.recv(request) connection.sendall(response)
    connection.close()

    相关文章

      网友评论

          本文标题:(1-2)socket -- sever

          本文链接:https://www.haomeiwen.com/subject/wlexixtx.html