基於golang的遠端監控web伺服器
阿新 • • 發佈:2018-12-22
最近搞了搞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連線後不斷接受影象,並儲存之。
另外再開一個網頁服務,通過定時器不斷重新整理網頁圖片圖片。注意這裡重新整理圖片的時候在地址後面加了隨機引數,因為瀏覽器訪問相同地址的時候會從快取讀取,所以這樣才能保證圖片的不斷重新整理。
另外開了一個檔案伺服器,把圖片的儲存地址暴露出來。否則的話是無法訪問到圖片的。
執行效果: