Упрощенный аналог 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; } } }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д