1. 程式人生 > >基於golang的遠端監控web伺服器

基於golang的遠端監控web伺服器

最近搞了搞golang web ,接上一個監控程式,既然已經有了伺服器了,那麼直接做一個網頁,把監控影象顯示到網頁上吧。

PC端還是一樣的,通過opencv讀攝像頭,把影象逐個畫素通過TCP傳送到伺服器。

#include<opencv2\opencv.hpp>
#include <math.h>
#include <process.h>
#include "winsock.h"
//socket庫的lib
#pragma comment(lib,"ws2_32.lib")

#define PORT 9999

using namespace cv;
using namespace std;

SOCKET socksvr, tcpsockclient;
CHAR szRecv[1472] = { 0 };
CHAR szSend[1472] = { 0 };
struct sockaddr_in clientaddr = { 0 };
struct sockaddr_in tcpclientaddr = { 0 };
int nLen = sizeof(clientaddr);
int tcpnLen = sizeof(tcpclientaddr);
volatile HANDLE udpreceive, tcpreceive;
UINT threadid, tcpthreadid;
int start = 0;


void delay(int s)
{
	while (s > 0)s--;
}
void TCPServerInit()
{
	tcpsockclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == tcpsockclient)
	{
		return;
	}
	/*************建立伺服器端套接字地址***********************/
	/********************繫結IP和埠號******************/
	struct sockaddr_in svraddr = { 0 };
	svraddr.sin_family = AF_INET;//代表internet協議族
	svraddr.sin_port = htons(PORT);
	//htonl()函式是將u_long型變數從主機位元組順序變為TCP/IP網路位元組順序。
	svraddr.sin_addr.S_un.S_addr = inet_addr("120.79.34.31");//inet_addr("127.0.0.1");//htonl(INADDR_ANY);//此巨集為0,當前機器上任意IP地址,也可以指定當前機的ip和埠。//127.0
	//svraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");												 //繫結,將伺服器端套接字與伺服器端套接字地址繫結
	int iRetVal = connect(tcpsockclient, (struct sockaddr*)&svraddr, sizeof(svraddr));
	if (SOCKET_ERROR == iRetVal)
	{
		printf("伺服器連線失敗!");
		closesocket(tcpsockclient);
		return;
	}
	printf("伺服器連線成功!\n");
}
void UDPServerInit()
{
	//建立socket
	socksvr = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == socksvr)
	{
		return;
	}
	//伺服器套接字地址
	//繫結ip與埠,先定義埠等一些資訊。
	struct sockaddr_in svraddr = { 0 };
	svraddr.sin_family = AF_INET;
	svraddr.sin_port = htons(PORT);
	svraddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	bind(socksvr, (struct sockaddr*)&svraddr, sizeof(svraddr));

}



void TCPSendImage(Mat image)
{
	//int i;
	//int datanum;
	//send(tcpsockclient, "ss", 2, 0);//傳送函式。
	//sendto(socksvr, "ss", 2, 0, (struct sockaddr*)&clientaddr, nLen);
	//if (start !=0)
		send(tcpsockclient, (const char*)image.data, image.cols*image.rows, 0);
		//while (1);
	//for (int i = 0; i < image.rows; i ++)
	//{
	//	send(tcpsockclient, (const char*)image.ptr<uchar>(i), image.cols, 0);//傳送函式。
		//sendto(socksvr, (const char*)image.ptr<uchar>(i), image.cols * 2, 0, (struct sockaddr*)&clientaddr, nLen);
		//datanum=recvfrom(socksvr, szRecv, sizeof(szRecv), 0, (struct sockaddr*)&clientaddr, &nLen);

		//delay(100000);
	//}
	//send(tcpsockclient, "ee", 2, 0);//傳送函式。
	//sendto(socksvr, "ee", 2, 0, (struct sockaddr*)&clientaddr, nLen);
	//datanum = recvfrom(socksvr, szRecv, sizeof(szRecv), 0, (struct sockaddr*)&clientaddr, &nLen);
}


void SendImage(Mat image)
{
	int i;
	int datanum;
	sendto(socksvr, "ss", 2, 0, (struct sockaddr*)&clientaddr, nLen);
	for (i = 0; i < image.rows; i += 2)
	{
		sendto(socksvr, (const char*)image.ptr<uchar>(i), image.cols * 2, 0, (struct sockaddr*)&clientaddr, nLen);
		//datanum=recvfrom(socksvr, szRecv, sizeof(szRecv), 0, (struct sockaddr*)&clientaddr, &nLen);

		delay(200000);
	}
	sendto(socksvr, "ee", 2, 0, (struct sockaddr*)&clientaddr, nLen);
	//datanum = recvfrom(socksvr, szRecv, sizeof(szRecv), 0, (struct sockaddr*)&clientaddr, &nLen);
}

UINT WINAPI TCPListenThread(void* pParam)
{
	int datanum;
		while (1) {
			if (start == 0) {
				datanum = recv(tcpsockclient, szRecv, sizeof(szRecv), 0); //接收函式,一直處於偵聽模式,等待伺服器端傳送資料的到來。//構造ip地址
				printf("Recieve:%s From:%s:%d\n", szRecv, inet_ntoa(tcpclientaddr.sin_addr), ntohs(tcpclientaddr.sin_port));
				if (datanum == 2 && szRecv[0] == 's'&&szRecv[1] == 's')
				{
					start = 2;
					printf("Start!\n");
				}
			}
			else if (start == 2) {
				datanum = recv(tcpsockclient, szRecv, sizeof(szRecv), 0); //接收函式,一直處於偵聽模式,等待伺服器端傳送資料的到來。//構造ip地址
				if (datanum == 2 && szRecv[0] == 'e'&&szRecv[1] == 'e')
				{
					start = 0;
					printf("End!\n");
				}
			}


	}
	CloseHandle(tcpreceive);
	return 0;
}
UINT WINAPI UDPListenThread(void* pParam)
{

	int datanum;
	printf("UDP listening!\n");
	while (1)
	{

		if (start == 0) {
			datanum = recvfrom(socksvr, szRecv, sizeof(szRecv), 0, (struct sockaddr*)&clientaddr, &nLen);//構造ip地址
			printf("Recieve:%s From:%s:%d\n", szRecv, inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
			if (datanum == 2 && szRecv[0] == 's'&&szRecv[1] == 's')
			{
				start = 1;
				printf("Start!\n");
			}
		}
		else if (start == 1) {
			datanum = recvfrom(socksvr, szRecv, sizeof(szRecv), 0, (struct sockaddr*)&clientaddr, &nLen);//構造ip地址
			if (datanum == 2 && szRecv[0] == 'e'&&szRecv[1] == 'e')
			{

				start = 0;
				printf("End!\n");
			}
		}
		//if(!(szRecv[0] == 's'&&szRecv[1] == 's')&&!(szRecv[0] == 'e'&&szRecv[1] == 'e'))
		//{
		//	sprintf(szSend, "Received %d bytes data!\n", datanum);
		//	sendto(socksvr, szSend, sizeof(szSend), 0, (struct sockaddr*)&clientaddr, nLen);//傳送時構造ip地址和埠。
		//}

	}
	CloseHandle(udpreceive);
	return 0;
}


int main(int argc, char** argv) {
	WSADATA wsa = { 0 }; //WinSockApi 取WSA+DATA組成套接字結構體
	WSAStartup(MAKEWORD(2, 2), &wsa);
	UDPServerInit();
	TCPServerInit();
	udpreceive = (HANDLE)_beginthreadex(NULL, 0, UDPListenThread, 0, 0, &threadid);
	tcpreceive = (HANDLE)_beginthreadex(NULL, 0, TCPListenThread, 0, 0, &tcpthreadid);
	//DrawCir();
	Mat img, gray;
	char key;
	VideoCapture cap(0);
	if (!cap.isOpened()) return -1;
	VideoWriter me("dir.avi", CV_FOURCC('M', 'J', 'P', 'G'), 20, { 640, 480 });
	int startre = 0;
	while (1)
	{
		cap >> img;
		namedWindow("circles", 1);
		cvtColor(img, gray, CV_BGR2GRAY);
		resize(gray, gray, Size(320, 240));
		imshow("circles", gray);
		if (startre)me << img;
		if (start == 1) {
			SendImage(~gray);
		}
		else if (start == 2) {
			TCPSendImage(gray);
		}
		key=waitKey(1);
		if (key == 's')start = 2;
		else if (key == 'd')start = 0;
	}
	//
	//清理套接字資源
	closesocket(socksvr);
	WSACleanup();
	return 0;
}

伺服器這邊需要做兩個事,一是接受影象,一是把影象釋出到網頁上。

這個事情用golang做起來其實挺簡單的。

所有程式碼只有這麼多:

package main

import (
	"fmt"
//	"html/template"
	"image"
	"image/color"
	"image/jpeg"
	"io"
	"net"
	"net/http"
	"os"
)

const (
	dx = 320
	dy = 240
)

func tcp() {
	tcpAddr, _ := net.ResolveTCPAddr("tcp4", ":9999") //獲取一個tcpAddr
	listener, _ := net.ListenTCP("tcp", tcpAddr)      //監聽一個埠
	alpha := image.NewAlpha(image.Rect(0, 0, dx, dy))
	in := make([]byte, dx*dy)
	number :=0
	for {
		fmt.Println("listening")
		conn, err := listener.AcceptTCP()
		if err != nil {
			continue
		}
		fmt.Println("remote addr:", conn.RemoteAddr())
		//conn.SetReadBuffer(5)
		//in :=make([]byte,640*480)
		//n,_ :=conn.Read([]byte(in))
		//in,_ :=ioutil.ReadAll(conn)
		//limi := io.LimitReader(conn,5)
		//n,_ := limi.Read(in)
		for {
			io.ReadFull(conn, in)
			for i := 0; i < dy; i++ {
				for j := 0; j < dx; j++ {
					alpha.SetAlpha(j, i, color.Alpha{uint8(in[i*dx+j])}) //設定alpha圖片的透明度i
			//		fmt.Print(uint8(in[i*dy+j]))
			//		fmt.Print(" ")
				}
			//	fmt.Println(i)
			}
			//fmt.Println(in)
			file, err := os.Create("./img/test.jpeg")
			if err != nil {
				fmt.Println(err)
			}
			jpeg.Encode(file, alpha, nil)
			file.Close()
			fmt.Println("get img",number)
		number++
		//	break
		}
		conn.Close()
	}

}
func login(w http.ResponseWriter, r *http.Request) {
	//t, _ := template.ParseFiles("index.html")
	//fmt.Println(t.Execute(w, nil))
w.Write([]byte(tpl))

}
const tpl = `<html>
<body>
<h1>This is a heading</h1>
    <img src="./img/test.jpeg" id="img" />

</body>
<script>
    var oImg = document.getElementById('img');
    var timer = null;
    var i = 2;
    timer = setInterval(function(){
        oImg.src = './img/test.jpeg?'+Math.random()+'='+Math.random();
        i++;
	if(i>10)i=0;
    },200);
</script>
</html>`
func main() {
	go tcp()
	http.HandleFunc("/", login) //設定訪問的路由
	http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("img"))))
	err := http.ListenAndServe(":777", nil) //設定監聽的埠
	if err != nil {
		fmt.Println("ListenAndServe: ", err)
	} //設定訪問的路由


這裡預設影象是320*240的灰度圖,並且是連續的。

先開一個go程執行TCP服務端,建立TCP連線後不斷接受影象,並儲存之。

另外再開一個網頁服務,通過定時器不斷重新整理網頁圖片圖片。注意這裡重新整理圖片的時候在地址後面加了隨機引數,因為瀏覽器訪問相同地址的時候會從快取讀取,所以這樣才能保證圖片的不斷重新整理。

另外開了一個檔案伺服器,把圖片的儲存地址暴露出來。否則的話是無法訪問到圖片的。

執行效果: