2019年1月

Python 实现简易HTTP服务器

深入了解http协议的话,从底层看就是socket套接字通信利用一个规定的协议去发送和接收消息,再根据浏览器的解析将数据显示在前台页面,核心就是socket通信,所以http服务器的模拟就很容易了,原理就是这样,接下来直接上代码,代码需要你把自己的网站文件放在与http_server.py同一目录下的html目录中才能运行成功:

1.源码

http_server.py

''' 实现简单的http服务器,多进程版 '''
import socket
import re
import multiprocessing

def service_client(new_socket):
   # 接收浏览器发送过来的请求,也就是http请求
   request = new_socket.recv(1024).decode("utf-8")
   # 将request头部拆分,并采用正则将请求的页面解析出来
   request_lines = request.splitlines()
   # 匹配正则
   ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0]).group(1)
   # 如果是根目录直接解析到主页index.html
   if ret == "/":
       ret = "/index.html"   
   try:
       # 打开文件
       f = open("./html" + ret, "rb") 
   except:
       response = "HTTP/1.1 404 NOT FOUND"
       response += "\r\n"
       response += "----404 not found----"
       new_socket.send(response.encode("utf-8"))
   else:
       # 回复header
       response = "HTTP/1.1 200 OK\r\n"
       response += "\r\n"
       # 得到body内容
       html_content = f.read()
       f.close()
       # 发送header与body
       new_socket.send(response.encode("utf-8"))
       new_socket.send(html_content)
   # 关闭套接字
   new_socket.close()
def main():
   # 1.创建套接字
   tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

   # 设置选项,让服务器结束之后立即释放资源
   tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

   # 2.绑定
   tcp_server_socket.bind(("", 7709))

   # 3.变为监听套接字
   tcp_server_socket.listen(128)

   # 4.等待新客户端的链接
   while True:

       new_socket, client_addr = tcp_server_socket.accept()

       # 5.为这个客户端服务
       p = multiprocessing.Process(target=service_client, args=(new_socket,))
       p.start()
       new_socket.close()

if __name__ == "__main__":
   main()

2.运行效果

3.Tips

QQ:770925351

Wechat:tc770925351

E-mail:770925351@qq.com

在近日的Python学习中,再次学习到了TCP协议中比较精髓的部分,3次握手与4次挥手,记得以前网络原理老师给讲解时候的声容并茂,当时明白了,但是没有有效的记录,很快就忘记了,现在重新温习了一遍,好记性不如烂笔头,有效的记录是提高学习效率的好方法

一、3次握手

所谓3次握手,就是在client与server建立连接时候,client与server之间进行3次通信来建立有效的连接,如图所示:

第一次:客户端要连接到服务器,向服务器发送SYN数据包,并发送一个序号为X的数据

第二次:服务器接收到客户端发来的SYN数据包,并发送一个ACK报文,报文里的值为X+1,同时向客户端发送一个序号为Y的数据

第三次:客户端接收到服务器发来的SYN数据包,并发送一个ACK报文,报文的值为Y+1,然后与服务器建立连接

总结起来就是这么三句话:

今晚有空吗?吃个饭呗

有空,去哪吃

好的,我知道了,听我安排吧

二、4次挥手

所谓4次挥手,就是客户端与服务器断开连接时候,需要进行4次通信,来真正释放资源,断开连接,如图所示:

第一次:客户端要断开连接了,向服务器发送数据包,表示自己不会再发消息给服务器,跟3次握手相似,发送一个X数据和Z数据

第二次:服务器收到客户端发送的数据包,并发送一个X+1数据和Z数据,并确认客户端不会再发来数据

第三次:服务器向客户端发送一个数据包,表示自己不会再发数据给客户端,并发送一个Y数据和Z数据

第四次:客户端向服务器发送一个数据包,发送Y+1Z数据,确认服务器不会再发来消息

为什么这里会有四次?不像连接时候的3次,因为如果当你套接字close的时候才会有断开请求,所以说断开是主动的,不能跟发送数据是主动,所以返回数据和断开请求是分开的,在服务器发出断开请求之后,服务器会有一个超时时间来等待客户端发送返回回来的数据,只要客户端一发送过来服务器就立马释放资源,此时会有一个超时时间,如果等待不来客户端的第四次挥手,那么服务器就会再次重新发送第三次挥手的内容,而客户端在接收到第三次挥手的数据之后会进行第四次挥手,为确保第四次挥手能够正确发送到服务器并让服务器释放资源,客户端会有一个2分钟的TIME_WAIT,此时他会一直等待,当服务器释放资源结束等待,所以主动断开连接的一端会占用资源一会,这也是为什么你的tcp服务器在进行ctrl+C中止的时候不能立马再运行第二遍,因为主动断开连接的一端会有一个TIME_WAIT

4次挥手可以设置这么一个场景:

两个熟人在车站相遇,但是要分别去往不同的地方,此时两人要分别了:

A:我走了啊,我要去xxx

B:我知道了,你走吧

B:我也要走了,我要去yyy

A:我知道了,咱俩分头走吧

最近刚找了个国企的工作,虽然挺稳定吧,也很轻松,但是不是自己想干的,果断跑路辞职,毕业设计也已经确定了题目,开始搞了起来,各方面工作都开始启动,希望能够完成自己的目标吧,好像有点扯远了,继续说一下今天的小程序,这是我闲的没事情干写了个小东西,毕竟我是个懒人嘛,查快递每次得打开浏览器,输入网址,输入快递单号,真是麻烦,这个事情能不能用命令行实现呢?有了这个想法之后,利用一小会时间实现了这个小玩意,下面就不多赘述,开始说一下自己的思路:

1.整体思路

我们在查快递的时候,其实在浏览器输入地址然后再输入快递单号本质就是向服务器发出了一个GET请求,只要去进行正确的参数请求即可,然而Python的requests库就可以实现所有常见的http请求,分析返回来的数据即可,把有用的东西留下,并展示在前台界面,这就实现了我们的需求,比较知名的快递查询网站就是快递100啦,接下来开始搞起

2.F12万能大法

有了思路要开始考虑写程序的事情了,对于这种请求一般都是网站有专门api去进行调用数据库返回json信息,这时候就开始我们的F12万能大法,打开快递100首页,如图所示,主页挺漂亮的:

按下F12,并在单号输入框中输入单号,这时候发现页面一直在向一个api请求:

返回的json字符串正是单号所对应的快递公司,一部分信息取到:

api:http://www.kuaidi100.com/autonumber/autoComNum

参数有:

resultv2:1

text:快递单号

接下来搜索:

又出现了一个新的请求,看来这个就是获取到物流信息的api呀,得来全不费功夫

api:http://www.kuaidi100.com/query

参数有:

type:快递公司名称

postid:快递单号

phone:手机号后四位

3.程序源码

得到两个重要的api之后,我们就可以很快速的写出这个程序来,下面是程序源码:

快递查询.py

import requests

'''获取快递编号所对应的快递公司名称'''
def get_type(headers, express_code):
    type_list = []
    type_url = "http://www.kuaidi100.com/autonumber/autoComNum"
    params = {
        'resultv2' : 1 ,
        'text' : express_code
    }
    r = requests.get(type_url, headers=headers, params=params)
    result = r.json()
    type_dicts = result['auto']
    for type_dict in type_dicts :
        type_list.append(type_dict['comCode'])
    return type_list

'''向用户展示查询到的快递公司'''
def show_type(type_list, type_name):
    print("该运单号可能为以下快递:")
    for express_type in type_list:
        print(type_name[express_type])
    express_type = input("请输入您的快递种类(请输入快递种类的简拼,例如'顺丰'为'shunfeng'):")
    return express_type

'''主函数,查询快递信息'''
def main():
    type_name = {
        'yuantong' : '圆通' ,
        'shunfeng' : '顺丰' ,
        'yunda' : '韵达' ,
        'baishihuitong' : '百事汇通' ,
        'shentong' : '申通' ,
        'zhogntong' : '中通' ,
        'tiantian' : '天天' ,
        'jingdong' : '京东'
    }
    express_code = input("请输入要查询的快递单号:")
    phone_num = input("请输入手机的后四位数字:")
    msg_url = "http://www.kuaidi100.com/query"
    headers = {
        'User-Agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
    }
    type_list = get_type(headers, express_code)
    express_type = show_type(type_list,type_name)
    params = {
        'type' : express_type ,
        'postid' : express_code ,
        'phone' : phone_num
    }
    r = requests.get(msg_url, headers=headers, params=params)
    result = r.json()
    message = result['message']
    status_code = result['status']
    if status_code == '200':
        datas = result['data']
        print("您所查询的快递信息如下:")
        for data in datas:
            print(data['time'] + ":" + data['context'])
    else:
        print(message)

if __name__ == "__main__":
    while True:
        main()
        i = input("是否继续查询?(任意字符为继续,q为退出)")
        if i == "q":
            print("感谢使用,祝您生活愉快,再见!")
            break

4.运行效果

5.后记

其实快递100提供了很多api接口去为用户提供快递查询服务,本次是用了原生爬虫的思路,可能会有些啰嗦,但毕竟亲自实践得来的东西才是最爽的,这种分析思路是爬虫必须具备的,小程序只是具备查询type_name字典里面的快递类型,因为快递种类实在太多,懒得写了,小伙伴如果有兴趣的可以在type_name添加你想要查询的类型,在api返回的是简拼,为了让输出的更加好看一点,所以才有了type_name这个东西,如果你api返回的类型不在字典里,程序就会报错,想避免这个问题,就直接把程序中的print(type_name[express_type])修改为print(express_type),这样程序就不会报错,但是会显示快递公司的简拼,总之,东西技术含量不高,用来练练手就可以

6.Tips

QQ:770925351

Email:770925351@qq.com