pythonメニュー

Webサーバの検討、Desktop画面を配信するWebサーバ

TCPを使って、HTTPサーバの簡易版を実現する例

MyWebServer.py で、接続してきたブラウザに固定のHTMLを送り返す。
ブラウザのURL欄に「http://127.0.0.1/」を入力して、接続します。
# MyWebServer.py
#このコード内に埋め込んである所定のHTMLだけを配信するWebサーバ
# 自作のsocketから作るHTTPサーバ

import socket
import threading

server_ip = "127.0.0.1"
server_port = 80
listen_num = 30

tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(type(tcp_server), "のソケットオブジェクトの作成")

tcp_server.bind((server_ip, server_port))
print("ソケットにIPアドレス:{}ポート:{}を紐づける". format(server_ip,server_port))

tcp_server.listen(listen_num)
print("listenで接続可能状態へ移行")

html='''<html>
<body>
<h1>TEST</h1>
</body>
<html>
'''

def rec_send(client): # 接続してきたブラウザの応答送信するスレッド用関数
    print("接続してきた相手の情報:{}型、{}".format(type( client ), client ) )
    head = ""
    while True:
        data = client.recv(1) # ブラウザからのリクエストメッセージ受信
        head +=data.decode()
        idx = head.find("\r\n")
        if idx != -1 :
           print( head , end="")
           if head == "\r\n" : break # ヘッド部終了
           head = ""
    #
    # HTTPヘッダー部のテンプレート文字列定義
    send_head='''HTTP/1.1 200 OK
Content-Type: text/html
Accept-Ranges: bytes
Content-Length: {}\r\n\r\n'''
    #
    body = html.encode('utf-8') # byteに変換
    send_head=send_head.format(len(body)) # Content-Lengthの値を埋め込む
    send_head = send_head.encode('utf-8') # byteに変換
    client.send(send_head) # HTTPヘッド部送信
    print(send_head.decode())
    client.send(body) # HTTPボディ部送信
    print(body.decode())
    client.close()


while True:
    client,address = tcp_server.accept()
    print("クライアント接続許可:",type(client), address)

    # rec_send関数を、clientを引数にスレッドで実行する。
    t_id = threading.Thread(target=rec_send, args=(client,))
    t_id.start()



デスクトップ画面を配信するWebサーバの作成

単にWebサーバを起動させ、同時にデスクトップの画像を1秒ごとに "scr0000.png"〜"scr9999.png"の名前で保存する別スレッドを起動さています。
別途で、"scr.png"の表示用index.htmlを用意することで、デスクトップ画面を配信するサーバとしています。
ブラウザから要求された時のイメージ取得操作と、1秒ごとキャプチャファイル保存操作を同じファイルにすると一方でアクセス例外が生じるます。
そこでブラウザから"scr.png"を要求した場合でキャプチャが必要な場合は、その時点に"scr.png"で保存して、それを配信します。
そして、1秒ごとキャプチャファイル保存用スレッドは、"tem.png"で保存して、scr{:04}.pngで保存しています。

# -*- coding: utf-8 -*-
import http.server
from http.server import HTTPServer, CGIHTTPRequestHandler
from socketserver import ThreadingMixIn 
import pyautogui as pag # デスクトップスクリーンを制御するモジュール
import threading
import numpy as np
import cv2
import time
import os
import shutil # シェル操作用モジュール(shell utility) 
if os.path.isdir("./imgs/") == True:
    shutil.rmtree('./imgs') # imgsフォルダを中身ごと削除
os.mkdir('./imgs') # imgsフォルダを新規作成

countup_flag=True # キー操作で、キャプチャの開始(True)、停止(False)を制御する

class Handler(CGIHTTPRequestHandler): # HTTP要求に対する処理クラスの継承
    count1=0
    body = None
    cgi_directories = ["/cgi-bin"]
    def do_GET(self):
        s=self.requestline
        print(s) # ブラウザからリクエスト
        i2 = s.find('.png')
        if i2 == -1 : return super().do_GET()
        i2 += len('.png')
        i1 = s.rfind('imgs/scr', 0, i2) # i2より前
        if i1 == -1 : return super().do_GET()
        request_file = s[i1:i2]  # 設定例 'imgs/scr.png' 
        # print( "★★★★★" , request_file)
        if Handler.count1 < time.time() : # 一定間隔でしかキャプチャしない制御
            Handler.count1 = time.time() + 1 # 次の実行を1秒後に設定
            img=pag.screenshot() # キャプチャー
            img=np.asarray(img)
            img=cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
            cv2.imwrite("imgs/scr.png", img) # 画像保存
        #
        with open(request_file, "rb") as f:
            Handler.body=f.read()
        #
        self.send_response(200)#成功レスポンス番号
        self.send_header('Cache-Control', 'no-store')
        self.send_header('Content-type', 'image/png')
        self.send_header('Content-length', len(Handler.body))
        self.end_headers()
        self.wfile.write(Handler.body)

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): 
    pass 

def stop_start():
    global countup_flag
    while True:
       s = input("'Q'の入力で終了>").upper()
       if s == 'Q':
           countup_flag = None
           return
       countup_flag = not countup_flag
       if countup_flag : print("★カウントを開始しました★")
       if not countup_flag : print("★カウントを停止しました★")

def capture():# スレッドで行うキャプチャの1秒ごとに繰り返し
    count=0
    while not countup_flag is None:
        try:
           time.sleep(1) # 1秒待機
           if not countup_flag : continue 
           img=pag.screenshot() # キャプチャ
           img=np.asarray(img)
           img=cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
           cv2.imwrite("imgs/tmp.png", img) # 保存
           filepath = "imgs/scr{:04}.png".format(count)
           shutil.copy("imgs/tmp.png",filepath)
           print("  生成ファイル++++++++++++++++++++++++++++++++++++:", filepath)
           count+=1
        except Exception as e:
           print(e)
        #
    #
    print("終了")

t_id = threading.Thread(target=capture)
t_id.start()
t_id2 = threading.Thread(target=stop_start)
t_id2.start()

server_address = ("", 9000)
handler_class = Handler #ハンドラを設定
my_webserver = ThreadedHTTPServer(server_address, handler_class)
my_webserver.serve_forever()



上記サーバ用のindex.html

タイマーによる自動更新で、サーバ側のデスクトップイメージ(img/scr.png)を取得して表示を繰り返します。
リンクで手動更新ページ(index.htm)へ移動できます。
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta http-equiv="Expires" content="0">

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width">
    <style>
        body {
            font-size: small;
            background-color:aquamarine;
            padding: 0;
            margin: 0;
        }
    </style>
    <title>Image </title>
<script type="text/javascript">
var timeId2;
var file="scr.png";
var img_elements;
function re_load(){
    var t = (new Date()).getTime() / 1000;
    t = Math.floor(t);
    img_elements.src = "imgs/scr.png";
}

function init(){
    img_elements= document.createElement("img");
    img_elements.onload= function (e) {
        var canvas = document.getElementById('CANV');
        canvas.width = document.documentElement.clientWidth -2;
        canvas.height = document.documentElement.clientHeight ;
        var ctx = document.getElementById('CANV').getContext('2d');
        ctx.drawImage(img_elements,0,0);
    };
    timeId2 = setInterval(re_load, 1000);//1秒ごとに実行
}

</script>
</head>
<body onload="init()">
<a href="index.htm">手動更新ページへ</a><br>
<canvas id="CANV" width="1024" height="1024" style="border: solid 1px #FF0000;"></canvas>
</body>
</html>


上記サーバ用のindex.htm

手動更新ページ(index.htm)です。
ボタン操作で、過去の画面の画像の要求が可能です。
リンクにより、自動更新するページ(index.html)へ移動できます。
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta http-equiv="Expires" content="0">

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width">
    <style>
        body {
            font-size: small;
            background-color:aquamarine;
            padding: 0;
            margin: 0;
        }
    </style>
    <title>Image </title>
<script type="text/javascript">
var current_N=0;
function re_load(){
	var img_elements= document.createElement("img");
    img_elements.onload= function (e) {
		document.getElementById("IMG_A").src = "";
		document.getElementById("IMG_A").src = img_elements.src;
		//設定ファイル名が同じ場合、キャッシュが使われて反映しない。?
		console.log(e.target.alt + " load");
        document.getElementById("CurrentImgName").textContent="scr.png";
        document.getElementById("CurrentImgName").style.backgroundColor="cyan";
	};
	img_elements.src = "imgs/scr.png";
    document.getElementById("CurrentImgName").style.backgroundColor="#444444";
}

function set_changeup(n){
    var nn = current_N + n;
    if( nn < 0 ) nn = 0; 
    path="00000"+nn;
    path = path.substring( path.length - 4);
    path = "imgs/scr" +path + ".png";
    var idx = path.indexOf("scr");
    var filename=path.substring(idx);

    var img_elements= document.createElement("img");
    img_elements.onload= function (e) {// 画像がロードできた時実行
        document.img_main.src=img_elements.src;
        document.getElementById("CurrentImgName").textContent=filename;
        current_N=nn;
        document.getElementById("CurrentImgName").style.backgroundColor="cyan"
	};
    img_elements.src=path;
    document.getElementById("CurrentImgName").style.backgroundColor="#888888";
}
</script>
</head>
<body>
<button onclick="re_load()">現在</button>
<button onclick="set_changeup(-100)">-100</button>
<button onclick="set_changeup(100)">+100</button>
<button onclick="set_changeup(-10)">-10</button>
<button onclick="set_changeup(10)">+10</button>
<button onclick="set_changeup(-1)">-1</button>
<button onclick="set_changeup(1)">+1</button>
<text id="CurrentImgName">scr.png</text>
<a href="index.html">自動更新ページへ</a> 
<span style="font-size: 8pt;">自分で更新処理をしないと画面が変化しません</span>
<br>
<img id="IMG_A" name="img_main" src="imgs/scr.png">
</body>
</html>