最近搞了搞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连接后不断接受图像,并保存之。
另外再开一个网页服务,通过定时器不断刷新网页图片图片。注意这里刷新图片的时候在地址后面加了随机参数,因为浏览器访问相同地址的时候会从缓存读取,所以这样才能保证图片的不断刷新。
另外开了一个文件服务器,把图片的保存地址暴露出来。否则的话是无法访问到图片的。
运行效果: