title: TCP Sending Simple Flood Control And Sync - P2P Webcam
body: <p>With the help of some nice people from SO I have slowly built a small P2P app that sends and receives a stream of images of about 4kb each.</p>
  
  <p>On 127.0.0.1 the receive keeps up with the sending, but when I try it on a remote machine it seems to me the receiving cannot keep up, I have perhaps sent 6 images but the receiver has only received one so far... and as time passes the difference gets bigger, until you see yourself a whole minute ago on the other screen. Its worth noting I would like this to work well on a connection that is about 64kbps-100kbps in another country where the ping times might be very big like 250ms or more.</p>
  
  <p>What are the synchronisation options I have?</p>
  
  <p>I have been advised by my brother of a simple solution which is to implement a 1:1 send/receive. So I only send an image when I receive one.</p>
  
  <p>As I am a total beginner in network programming, any other tips are most welcome, here is my complete code :</p>
  
  <pre><code>namespace MyPrivateChat
  {
      using System;
      using System.Drawing;
      using System.Windows.Forms;
      using AForge.Video;
      using AForge.Video.DirectShow;
      using System.Drawing.Imaging;
      using System.IO;
      using System.Net;
      using System.Text.RegularExpressions;
      using System.Net.Sockets;
      using System.Diagnostics;
      using AForge.Imaging.Filters;
  
      public partial class fChat : Form
      {
          public fChat()
          {
              InitializeComponent();
          }
          private void fChat_Load(object sender, EventArgs e)
          {
              // get ip
              _ownExternalIp = GetPublicIP();
              Text = "My Private Chat - IP: " + _ownExternalIp;
  
              // get video cam
              var _videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
              if (_videoDevices.Count != 0)
              {
                  _videoDevice = new VideoCaptureDevice(_videoDevices[0].MonikerString);
                  btnStart.Enabled = true;
              }
  
              // fire up listener
              listeningThread.RunWorkerAsync();
          }
          public string GetPublicIP()
          {
              string ip = "";
              using (WebClient wc = new WebClient())
              {
                  Match m = Regex.Match(wc.DownloadString("http://checkip.dyndns.org/"), @"(?&lt;IP&gt;\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})");
                  if (m.Success)
                  {
                      ip = m.Groups["IP"].Value;
                  }
              }
              return ip;
          }
          private void mnuPasteOwnIP_Click(object sender, EventArgs e)
          {
              txtPartnerIP.Text = _ownExternalIp;
          }
          private void btnStart_Click(object sender, EventArgs e)
          {
              if (_tcpOut == null)
              {
                  // tcp server setup
                  _tcpOut = new TcpClient();
                  _tcpOut.Connect(txtPartnerIP.Text, 54321);
                  tmrLive.Enabled = true;
              }
              else
              {
                  tmrLive.Enabled = false;
                  _tcpOut.Client.Disconnect(true);
                  _tcpOut.Close();
                  _tcpOut = null;
              }
  
              if (!_videoDevice.IsRunning)
              {
                  _videoDevice.NewFrame += new NewFrameEventHandler(NewFrameReceived);
                  _videoDevice.DesiredFrameSize = new Size(640, 480);
                  _videoDevice.DesiredFrameRate = 100;
                  _videoDevice.Start();
                  btnStart.Text = "Stop";
              }
              else
              {
                  _videoDevice.SignalToStop();
                  btnStart.Text = "Start";
              }
          }
          private void NewFrameReceived(object sender, NewFrameEventArgs e)
          {
              Bitmap img = (Bitmap)e.Frame.Clone();
              byte[] imgBytes = EncodeToJpeg(img, 25).ToArray();
  
              if (_tcpOut.Connected)
              {
                  NetworkStream ns = _tcpOut.GetStream();
                  if (ns.CanWrite)
                  {
                      ns.Write(BitConverter.GetBytes(imgBytes.Length), 0, 4);
                      ns.Write(imgBytes, 0, imgBytes.Length);
                      _totalFramesSent++;
                  }
              }
          }
          private void listeningThread_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
          {
              _tcpIn = new TcpListener(IPAddress.Any, 54321);
              _tcpIn.Start();
  
              TcpClient _inClient = _tcpIn.AcceptTcpClient();
              lblStatus.Text = "Connected - Receiving Broadcast";
              tmrLive.Enabled = true;
  
              NetworkStream ns = _inClient.GetStream();
              while (true)
              {
                  // read image size. 
                  Byte[] imgSizeBytes = new Byte[4];
                  int totalBytesRead = 0;
                  do
                  {
                      int bytesRead = ns.Read(imgSizeBytes, totalBytesRead, 4 - totalBytesRead);
                      if (bytesRead == 0)
                      {
                          break; // problem
                      }
                      totalBytesRead += bytesRead;
                  } while (totalBytesRead &lt; 4);
  
                  // read image
                  int imgSize = BitConverter.ToInt32(imgSizeBytes, 0);
                  Byte[] imgBytes = new Byte[imgSize];
                  totalBytesRead = 0;
                  do
                  {
                      int bytesRead = ns.Read(imgBytes, totalBytesRead, imgSize - totalBytesRead);
                      if (bytesRead == 0)
                      {
                          break; // problem
                      }
                      totalBytesRead += bytesRead;
                  } while (totalBytesRead &lt; imgSize);
  
                  picVideo.Image = Image.FromStream(new MemoryStream(imgBytes));
                  _totalFramesReceived++;
              }
          }
          private void CloseVideoDevice()
          {
              if (_videoDevice != null)
              {
                  if (_videoDevice.IsRunning)
                  {
                      _videoDevice.SignalToStop();
                  }
                  _videoDevice = null;
              }
          }
          private void fChat_FormClosing(object sender, FormClosingEventArgs e)
          {
              CloseVideoDevice();
          }
          private void btnClose_Click(object sender, EventArgs e)
          {
              Close();
          }
          private void tmrLive_Tick(object sender, EventArgs e)
          {
              _totalSecondsLive++;
              lblStats.Text = "S:"+_totalFramesSent + " R:" + _totalFramesReceived + " T:"+ _totalSecondsLive;
              if (_totalSecondsLive == 60)
              {
                  MessageBox.Show("Total Frames : " + _totalFramesSent);
              }
          }
  
          #region ENCODING JPEG
  
          private MemoryStream EncodeToJpeg(Bitmap img, long quality)
          {
              using (EncoderParameters myEncoderParameters = new EncoderParameters(1))
              {
                  MemoryStream ms = new MemoryStream();
                  myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
                  img.Save(ms, GetEncoder(ImageFormat.Jpeg), myEncoderParameters);
                  return ms;
              }
          }
          private ImageCodecInfo GetEncoder(ImageFormat format)
          {
              ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
              foreach (ImageCodecInfo codec in codecs)
              {
                  if (codec.FormatID == format.Guid)
                  {
                      return codec;
                  }
              }
              return null;
          }
  
          #endregion
  
          VideoCaptureDevice _videoDevice;
          TcpClient _tcpOut;
          TcpListener _tcpIn;
          string _ownExternalIp;
          int _totalFramesSent;
          int _totalFramesReceived;
          int _totalSecondsLive;
      }
  }
  </code></pre>