Python网络编程

socket、TCP、HTTP

Posted by 新宇 on January 7, 2020

一、网络简介

1. 网络的定义

信息传递和共享虚拟平台

2. ip

网络设备的唯一编号 ip分类

  • ipv4
  • ipv6:可编号的网络设备数目大

3.ip和域名的关系

域名解析 www.baidu.com<==>35.156.69.79

4. ip相关命令

ifconfig ping

5.端口和端口号

端口:数据传输的通道

端口号:端口的编号 0-65535 分类:

  • 知名端口号: 0-1023 , 不可占用,已经分配给了特定的服务,例如 22 80 等
  • 动态端口号: 1024-65535 , 程序员可以绑定,没有绑定会自动分配

二. socket

1.socket定义

网络通讯的工具

  • tcp
  • udp

2.tcp

面向连接的、可靠的、基于字节流的传输层通信协议

  1. 三次握手机制:

  2. 可靠性:
    • 采用发送应答机制
    • 超时重传
    • 错误校验
    • 流量控制和阻塞管理
  3. 客户端请求流程:

3.编码转换

# 编码
str.encode(encoding=utf-8)
# 解码
str.decode(encoding=utf-8)

4.socket客户端和服务端的实现

  1. 客户端实现

     import socket
    
     if __name__ == '__main__':
         # AF_INET 代表使用ipv4
         # SOCK_STREAM表示基于TCP协议
    
         client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         # 这里绑定IP和端口
    
         client.bind(('localhost', 8080))
         # 这里配置要连接的服务端地址和端口号
    
         client.connect(('localhost', 8081))
         while True:
             text = input('请输入要传输的数据:')
             # 向服务端发送数据
    
             client.send(text.encode(encoding='utf-8'))
             # 接受服务端返回的数据,参数表示定要接收的最大数据量
    
             msg = client.recv(1024)
             msg = msg.decode(encoding='utf-8')
             print('服务端返回:', msg)
             if msg == 'quit':
                 print('服务端请求断开连接!!!')
                 break
         # 关闭客户端socket
    
         client.close()
    
  2. 服务端实现(单线程版):

     import socket
    
     if __name__ == '__main__':
         # 创建server端套接字
    
         server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    	    
         # 设置端口复用
        	
     	server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
         # 绑定IP和端口
    
         server.bind(('', 8081))
         # 设置端口复用
    
         server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
         # 设置最大等待建立连接的个数
    
         server.listen(10)
         # 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞
         # 1. 专门和客户端通信的套接字: service_client_socket
         # 2. 客户端的ip地址和端口号: ip_port
    
         server_client_socket, ip_port = server.accept()
         print(server_client_socket)
         print('客户端的ip地址和端口号:', ip_port)
         while True:
             # 接收客户端发送的数据, 这次接收数据的最大字节数是1024
    
             msg = server_client_socket.recv(1024)
             msg = msg.decode(encoding='utf-8')
             print('接收到客户端发来的数据:', msg)
             if msg == 'quit':
                 print('客户端请求断开连接!!!')
                 break
             text = input('请输入要返回给客户端的信息;')
             server_client_socket.send(text.encode(encoding='utf-8'))
         # 关闭服务与客户端的套接字, 终止和客户端通信的服务
    
         server_client_socket.close()
         # 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务
    
         server.close()
    
  3. 服务端(多线程版):

     import socket
     import threading
    
    
     def process_data(server_client_socket, ip_port):
         while True:
             # 接收客户端发送的数据, 这次接收数据的最大字节数是1024
    
             msg = server_client_socket.recv(1024)
             msg = msg.decode(encoding='utf-8')
             print('接收到客户端发来的数据:', msg)
             if msg:
                 # text = input('请输入要返回给客户端的信息;')
                 server_client_socket.send('ok,问题正在处理中...'.encode(encoding='utf-8'))
             else:
                 print('客户端请求断开连接!!!', ip_port)
                 break
         # 关闭服务与客户端的套接字, 终止和客户端通信的服务
    
         server_client_socket.close()
    
    
     if __name__ == '__main__':
         # 创建套接字dict,用来复用
    
         # 创建server端套接字
    
         server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    	    
         # 设置端口复用
        	
     	server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    
         # 绑定IP和端口
    
         server.bind(('', 8081))
         # 设置端口复用
    
         server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
         # 设置最大等待建立连接的个数
    
         max_num = 2
         server.listen(max_num)
         # 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞
    
         # 1. 专门和客户端通信的套接字: service_client_socket
    
         # 2. 客户端的ip地址和端口号: ip_port
    
         for i in range(max_num):
             server_client_socket, ip_port = server.accept()
             print(server_client_socket)
             print('客户端的ip地址和端口号:', ip_port)
             t = threading.Thread(target=process_data, args=(server_client_socket, ip_port))
             t.setDaemon(True)
             t.start()
    
    
         # 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务
    
         # server.close()
    
    

5. send和recv原理剖析

三.HTTP

1. URL

统一资源定位符 协议: http, https, ftp ,规定数据发送和接收的格式 域名: www.baidu.com , 找到服务端IP, 端口默认80,可以不写 资源路径: /news/ 查询参数: /pic?path=xxx.jpg

2.HTTP协议

  1. 定义:超文本传输协议,用在http服务器和web浏览器之间的传输
  2. 请求方式
    • GET
    • POST
  3. 请求报文格式 请求行:请求方式 资源路径 协议版本 请求头:请求信息 请求体:封装好的请求信息(GET类型不需要)

  4. 响应报文 GET和POST请求响应的报文格式是相同的 响应行:协议版本 状态码(200:成功 4XX:资源不存在 5XX:服务器内部错误) 响应头:请求信息 响应体:封装好的响应信息

3.查看HTTP请求流程

再谷歌浏览器中,右键检查,点击Network

4. 自己搭建静态服务器

  1. Python自带的http服务器

     python3 -m http.server
    
  2. 自己搭建静态服务器
    • 创建TCP服务器
    • 接收浏览器访问数据
    • 解析浏览器发过来的请求(资源路径)
    • 根据解析的字眼路径,获取路径资源
    • 按照响应报文方式组织响应数据,传递回去(响应体)
    • 关闭连接
  3. web服务器返回指定页面

  4. 多任务版服务器

     import socket
     import threading
    
     def work(client_socket):
         while True:
             data = client_socket.recv(1024).decode()
             print(data)
             if not data:
                 break
             array = data.split(' ')
             uri = array[1]
             if uri == '/':
                 uri = '/index.html'
             try:
                 with open('./static' + uri, 'rb') as f:
                     res_data = f.read()
             except:
                 res_line = 'HTTP/1.1 404 Not Found\r\n'
                 res_header = 'server: python\r\n'
                 res_body = "404 Not Found, Sorry!"
                 res = (res_line + res_header + '\r\n' + res_body).encode()
                 client_socket.send(res)
             else:
                 res_line = 'HTTP/1.1 200 OK\r\n'
                 res_header = 'server: python\r\n'
                 res_body = res_data
                 res = (res_line + res_header + '\r\n').encode() + res_body
                 client_socket.send(res)  
         client_socket.close()
    
    
     if __name__ == '__main__':
         server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
         server_socket.bind(("", 8080))
         server_socket.listen(128)
         while True:
             client_socket, ip_port = server_socket.accept()
             print('ip_port:', ip_port)
             t = threading.Thread(target=work, args=(client_socket,))
             t.setDaemon(True)
             t.start()
         server_socket.close()
    
    
  5. 面向对象版本

    通过sys.argv来获取命令行参数

     import socket
     import threading
     import sys
    
     class WebServer():
         def __init__(self, port):
             self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
             self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
             self.server_socket.bind(("", int(port)))
             self.server_socket.listen(128)
    
         def start(self):
             while True:
                 client_socket, ip_port = self.server_socket.accept()
                 print('ip_port:', ip_port)
                 t = threading.Thread(target=self.work, args=(client_socket,))
                 t.setDaemon(True)
                 t.start()
             self.server_socket.close()
    
         def work(self, client_socket):
             while True:
                 data = client_socket.recv(1024).decode()
                 print('客户端发送:', data)
                 if not data:
                     break
                 array = data.split(' ')
                 uri = array[1]
                 print(uri)
                 if uri == '/':
                     uri = '/index.html'
                 try:
                     with open('./static' + uri, 'rb') as f:
                         res_data = f.read()
                 except:
                     res_line = 'HTTP/1.1 404 Not Found\r\n'
                     res_header = 'server: python\r\n'
                     res_body = "404 Not Found, Sorry!"
                     res = (res_line + res_header + '\r\n' + res_body).encode()
                     client_socket.send(res)
                 else:
                     res_line = 'HTTP/1.1 200 OK\r\n'
                     res_header = 'server: python\r\n'
                     res_body = res_data
                     res = (res_line + res_header + '\r\n').encode() + res_body
                     client_socket.send(res)
                 finally:
                     client_socket.close()
    
     def main():
         arr = sys.argv
         print(arr)
         if len(arr) < 2:
             s = WebServer(8080)
             s.start()
             return
         port = arr[1]
         if port.isdigit():
             s = WebServer(port)
             s.start()
             return
         else:
             print('命令格式有误!')
    
    
     if __name__ == '__main__':
         main()