Упрощенный аналог ngrok - Трех или четырехзвенная архитектура? - C#
Формулировка задачи:
тааак... короче, есть задумка создать упрощенный аналог ngrok. Для своих нужд) есть комп №1, есть комп №2, есть внешний сервер со статическим IP. Комп 1 и 2 находятся в разных сегментах сети, которые оба имеют доступ в инет, например, через роутер. на компе №1 запускаю приложение, которое коннектится к серваку на заранее известный порт, на компе №2 запускается приложение, которое так же коннектится к серваку, но на неопределенный порт (использую стороннюю библиотеку). На серваке крутится третье приложение, которое перекидывает данные из одного сокета в другой, но вот незадача - при передаче потока http данных от компа №1 до приложения на компе №2 (скажем, mjpg поток) после передачи одного фрейма приложение на компе №1 перестает генерить и передавать этот поток. Код:
Код класса ScreenWork
Код класса MjpegWriter
static void ConnectAndProcess()
{
try
{
_client.Connect(IPAddress.Parse(externIp), port);
}
catch (Exception)
{
_client = new TcpClient(AddressFamily.InterNetwork);
}
if (_client.Connected)
{
Console.WriteLine("Подключился");
using (var ns = _client.GetStream())
{
// так сервер определит, какой сокет является источником данных
var txtToSend = Encoding.ASCII.GetBytes("client");
ns.Write(txtToSend, 0, txtToSend.Length);
try
{
int count;
var buffer = new byte[1024 * 512];
while ((count = ns.Read(buffer, 0, buffer.Length)) > 0)
{
var str = Encoding.ASCII.GetString(buffer, 0, count);
Console.WriteLine("Принято от сервера {0} байт:\n{1}", count, str);
if (str.Contains("start"))
{
Console.WriteLine("Начинаю вещать видеопоток");
using (var writer = new MjpegWriter(ns))
{
_videoStreaming = true;
writer.WriteHeader();
foreach (var imgStream in ScreenWork.Snapshots(true).Streams())
{
Thread.Sleep(50);
writer.Write(imgStream);
Console.WriteLine("Sended " + imgStream.ToArray().Length);
}
}
}
}
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("{0} Ошибка сокета. Exception {1}", DateTime.Now, ex.Message));
}
}
}
else Trace.WriteLine(string.Format("{0} Не удалось подключиться", DateTime.Now));
Trace.WriteLine(string.Format("{0} Подключение окончено, требуется переподключение", DateTime.Now));
}public static class ScreenWork
{
public static IEnumerable<Image> Snapshots()
{
return Snapshots(true);
}
public static Size ImgSize { get; set; }
/// <summary>
/// Returns a
/// </summary>
/// <param name="showCursor"></param>
/// <returns></returns>
public static IEnumerable<Image> Snapshots(bool showCursor)
{
if (ImgSize.Width == 0 || ImgSize.Height == 0)
{
ImgSize = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
}
Size size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Bitmap srcImage = new Bitmap(size.Width, size.Height);
Graphics srcGraphics = Graphics.FromImage(srcImage);
bool scaled = (ImgSize.Width != size.Width || ImgSize.Height != size.Height);
Bitmap dstImage = srcImage;
Graphics dstGraphics = srcGraphics;
if (scaled)
{
dstImage = new Bitmap(ImgSize.Width, ImgSize.Height);
dstGraphics = Graphics.FromImage(dstImage);
}
var src = new Rectangle(0, 0, size.Width, size.Height);
var dst = new Rectangle(0, 0, ImgSize.Width, ImgSize.Height);
var curSize = new Size(32, 32);
while (true)
{
srcGraphics.CopyFromScreen(0, 0, 0, 0, size);
if (showCursor)
Cursors.Default.Draw(srcGraphics, new Rectangle(Cursor.Position, curSize));
if (scaled)
dstGraphics.DrawImage(srcImage, dst, src, GraphicsUnit.Pixel);
yield return dstImage;
}
srcGraphics.Dispose();
dstGraphics.Dispose();
srcImage.Dispose();
dstImage.Dispose();
yield break;
}
public static IEnumerable<MemoryStream> Streams(this IEnumerable<Image> source)
{
var ms = new MemoryStream();
foreach (var img in source)
{
ms.SetLength(0);
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
yield return ms;
}
ms.Close();
ms = null;
yield break;
}
}public class MjpegWriter:IDisposable
{
private static byte[] _crlf = new byte[] { 13, 10 };
private static byte[] _emptyLine = new byte[] { 13, 10, 13, 10};
private string _boundary;
public MjpegWriter(Stream stream)
: this(stream, "--boundary")
{
}
public MjpegWriter(Stream stream,string boundary)
{
this.Stream = stream;
this.Boundary = boundary;
}
public string Boundary { get; private set; }
public Stream Stream { get; private set; }
public void WriteHeader()
{
Write(
"HTTP/1.1 200 OK\r\n" +
"Content-Type: multipart/x-mixed-replace; boundary=" +
this.Boundary +
"\r\n"
);
this.Stream.Flush();
}
public void Write(Image image)
{
MemoryStream ms = BytesOf(image);
this.Write(ms);
}
public void Write(MemoryStream imageStream)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine(this.Boundary);
sb.AppendLine("Content-Type: image/jpeg");
sb.AppendLine("Content-Length: " + imageStream.Length.ToString());
sb.AppendLine();
Write(sb.ToString());
imageStream.WriteTo(this.Stream);
Write("\r\n");
this.Stream.Flush();
}
private void Write(byte[] data)
{
this.Stream.Write(data, 0, data.Length);
}
private void Write(string text)
{
byte[] data = BytesOf(text);
this.Stream.Write(data, 0, data.Length);
}
private static byte[] BytesOf(string text)
{
return Encoding.ASCII.GetBytes(text);
}
private static MemoryStream BytesOf(Image image)
{
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms;
}
public string ReadRequest(int length)
{
byte[] data = new byte[length];
int count = this.Stream.Read(data,0,data.Length);
if (count != 0)
return Encoding.ASCII.GetString(data, 0, count);
return null;
}
#region IDisposable Members
public void Dispose()
{
try
{
if (this.Stream != null)
this.Stream.Dispose();
}
finally
{
this.Stream = null;
}
}
#endregion
}Решение задачи: «Упрощенный аналог ngrok - Трех или четырехзвенная архитектура?»
textual
Листинг программы
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace MainServer
{
class Program
{
static System.Net.IPEndPoint _point1, _point2;
static tcpServer.TcpServer tcpServer;
static void Main(string[] args)
{
tcpServer = new tcpServer.TcpServer();
tcpServer.Port = 5000;
tcpServer.OnDataAvailable += tcpServer_OnDataAvailable;
tcpServer.Open();
while (true) {
System.Threading.Thread.Sleep(10);
}
}
static int indexPoint1, indexPoint2;
static int countChangesPoint2;
static void tcpServer_OnDataAvailable(tcpServer.TcpServerConnection connection)
{
var remoteIp = (IPEndPoint)connection.Socket.Client.RemoteEndPoint;
byte[] byteData = readStream(connection.Socket);
if (byteData != null)
{
if (byteData.Length == 6 && Encoding.ASCII.GetString(byteData) == "client")
{
_point1= remoteIp;
int i = 0;
foreach (var conn in tcpServer.Connections)
if (EqEndPoints((IPEndPoint)conn.Socket.Client.RemoteEndPoint, _point1))
{
conn.Socket.ReceiveBufferSize = 1024 * 512;
indexPoint1= i;
break;
}
else i++;
return;
}
if (_point2== null && byteData.Length == 7 && Encoding.ASCII.GetString(byteData) == "point12")
{
_point2 = remoteIp;
int i = 0;
foreach (var conn in tcpServer.Connections)
if (EqEndPoints((IPEndPoint)conn.Socket.Client.RemoteEndPoint, _point2))
{
indexPoint2 = i;
break;
}
else i++;
return;
}
if (countChangesPoint2 < 2 && _point2!= null && !EqEndPoints(remoteIp, _point2))
{
countChangesPoint2++;
_point2= remoteIp;
int i = 0;
foreach (var conn in tcpServer.Connections)
if (EqEndPoints((IPEndPoint)conn.Socket.Client.RemoteEndPoint, _point2))
{
indexPoint2 = i;
break;
}
else i++;
}
if (_client != null && _watcher != null)
{
if (EqEndPoints(remoteIp, _point1))
{
tcpServer.Connections[indexPoint2].sendData(Encoding.ASCII.GetString(byteData));
}
if (EqEndPoints(remoteIp, _point2))
{
tcpServer.Connections[indexPoint1].sendData(Encoding.ASCII.GetString(byteData));
}
}
}
}
protected static byte[] readStream(TcpClient client)
{
NetworkStream stream = client.GetStream();
List<byte> lst = new List<byte>();
while (stream.DataAvailable)
{
byte[] byteArr = new byte[1024 * 512];
int s = stream.Read(byteArr, 0, 1024 * 512);
Array.Resize(ref byteArr, s);
lst.AddRange(byteArr);
//call stream.Read(), read until end of packet/stream/other termination indicator
//return data read as byte array
}
return lst.ToArray();
}
static bool EqEndPoints(System.Net.IPEndPoint p1, System.Net.IPEndPoint p2)
{
if (p1 == null && p2 == null) return true;
if (p1 != null && p2 != null)
return p1.Address.ToString() == p2.Address.ToString() && p1.Port == p2.Port;
return false;
}
}
}