最近接到一个小任务,要为客户实现“web端实时控制嵌入式设备”的项目。主要要求是通过在公网部署,客户通过web页面操控,触发远程设备的相关指令,完成相应操作。

项目结构

项目设计的初期目标是,实现一个类似通道的通过,程序只负责转发数据,具体的业务由远程设备和web端协商。下面是项目的结构图

http2tcp.png

服务启动,实施对应用层http和传输层tcp的监听,远程设备此时可以通过TCP建立双向连接。HTTP服务收到web请求,解析请求数据,然后将请求数据转发给TCP服务,由于TCP服务和远程设备是双向通信,因此web请求的数据就转发到远程设备,完成这个简单的传输过程。

实现过程

设置相关监听参数
host = "127.0.0.1"
http_port = 4654
tcp_port = 3212

用于进程保存远程设备的socket连接的
client_socks

然后分别建立HTTP和TCP的两个处理线程。

  • TCP线程
    线程处理函数startTCPServe,当有新的设备连接上来,通过

client_socks[clientsock.fileno()] = clientsock
保存socket连接,以便在HTTP处理线程中使用。

  • HTTP线程中

Python的SocketServer包中,针对WEB服务有一个非常好用的模块BaseHTTPServer,通过该模块可以很容易的建立WEB服务,解析请求参数。这里自定义一个HTTP处理类EVEHTTPHandler,do_POST处理post请求,通过rfile读取post消息体,转交给message_handler方法处理。如果client_socks有可用句柄,则将数据转发。

这样就完成了整个转发流程。

Python代码实现

#!/usr/bin/env python
# evenvi
# http to tcp server
# 2017/10/20

from time import ctime
import threading, socket, traceback
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

host = "127.0.0.1"
http_port = 4654
tcp_port = 3212

http_addr = (host, http_port)
tcp_addr = (host, tcp_port )

client_socks = {}


class EVEHTTPHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        buf = 'It works GET'
        self.protocol_version = 'HTTP/1.1'
        self.send_response(200)
        self.send_header("Transfer", "trans success")
        self.end_headers()

        self.wfile.write(buf)

    def do_POST(self):
        buf = 'It works POST'
        self.protocol_version = 'HTTP/1.1'
        self.send_response(200)
        self.send_header("Transfer", "trans success")
        self.end_headers()

        fd = self.rfile
        httpMessage = fd.read(int(self.headers['Content-Length']))

        self.message_handler(httpMessage)

        self.wfile.write(buf)


    def message_handler(self, message):
        if message is not None:
            print(message)

        for fd, sock in client_socks.items():
            # print "SOCKS " + str(type(sock))
            sock.sendall(message)





def startHTTPServe(addr):
    """Start HTTP Server"""
    print("Start HTTP SERVER")
    http_server = HTTPServer(addr, EVEHTTPHandler)
    http_server.serve_forever()


def startTCPServe(addr):
    """Start TCP Server"""
    print("Start TCP SERVER")
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(addr)
    s.listen(1)
    try:
        clientsock, clientaddr = s.accept()
    except:
        traceback.print_exc()

    while True:

        #process the connection

        try:
            print "New message from ", clientsock.getpeername()
            #Precess request here
            client_socks[clientsock.fileno()] = clientsock

            client_msg = clientsock.recv(1024)
            print(client_msg)
            clientsock.send("welcome to httptotcp server")
        except:
            traceback.print_exc()


        # try:
        #     clientsock.close()
        # except:
        #     traceback.print_exc()




threads = []
t1 = threading.Thread(target=startHTTPServe, args=(http_addr,))
threads.append(t1)
t2 = threading.Thread(target=startTCPServe, args=(tcp_addr,))
threads.append(t2)

if __name__ == '__main__':
    for t in threads:
        t.setDaemon(True)
        t.start()

    t.join()

    print "all over %s" % ctime()

测试

POSTMAN中:

POST http://127.0.0.1:4654 {"code":"1", "msg":"control-1"}

命令行终端使用NETCAT

nc 127.0.0.1 3212
然后就可以在终端中看到postman实时发送的数据

其他

这个只支持但设备,如果有需求可以通过扩展,以满足需求。

Tags: http和tcp交互, python

Related Posts:
  • [尚无相关文章]

Leave a Comment