Connect from C# to a digital scale - c#

I'm trying to figure out a method of connecting from C# code to a digital scale. The particular scale is an Ohaus SP202 digital scale which comes with a USB connection. I would like to read the weight measured on the scale programmatically. I don't have the scale yet, I'm just doing the research before hand.
Has anyone done this before? I've been doing research on the internet and didn't find anything worth mentioning yet.

USB Hardware communication popularly works one of three ways.
Proprietary software talks to hardware via proprietary driver.
Devices have a Serial emulation chip (e.g. FTDI) When you plug in the scale you just need to install the Virtual Comm Port drivers and the device shows up as a comm port on your system. Then it is as easy as using System.IO.Ports.SerialPort to talk to the device.
The device implements a HID profile and will be available through your OS's HID system. I've used this .NET HID library on Windows to successfully talk with barcode scanners that implement HID. The HID library will send data to you from the hardware in chunks that you decipher based on the hardware you are talking to.
With method 2 and 3 you will just need to find the data format for the scale that you are talking to. The scales that I have used send updates every second or so with the weight and other information shown on the hardware UI, such as if the load has stabilized or not.
Looking at their discussion forums it looks like their scales use method 2 (http://ohaus.com/support/forum_messages.asp?topicid=584) and that you need to Poll the scale by sending a "P\r\n" it will then respond with the characters that are shown on the display (http://ohaus.com/support/forum_messages.asp?topicid=802).

I haven't used that specific scale, but I have connected to other digital scales previously. Basically it is usually just doing serial communications via the USB to Com converter.
If the scale has an API for this all the better, but if not then you will just be using System.IO.Ports.SerialPort which is pretty standard serial programming. A starter article here

I don't know any details on this scale, but I've done some USB stuff.
Its most likely using usb interrupt to transfer the data. All usb mice also use interrupt, so if you can figure out how to read the mouse signal (with using an HID api), then it should be exactly the same as the scale, except the data format returned would be completely different.
run on ftw

Scales was one of the first devices I ever had to read data from. At that time, it was just a serial port and I'm guessing that you'd still be able to read from the serial port with SerialPort.Net.
Here's the code for Serial Port transfer on Windows
Reference
using Device.Net;
using Device.Net.Windows;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SerialPort.Net.Windows
{
public class WindowsSerialPortDevice : DeviceBase, IDevice
{
#region Fields
private readonly int _BaudRate;
private readonly byte _ByteSize;
private bool disposed;
private readonly Parity _Parity;
private SafeFileHandle _ReadSafeFileHandle;
private readonly StopBits _StopBits;
private ushort ReadBufferSize { get; }
#endregion
#region Public Properties
public bool IsInitialized => _ReadSafeFileHandle != null && !_ReadSafeFileHandle.IsInvalid;
/// <summary>
/// TODO: No need to implement this. The property probably shouldn't exist at the base level
/// </summary>
public IApiService ApiService { get; }
public ConnectedDeviceDefinition ConnectedDeviceDefinition { get; private set; }
#endregion
#region Constructor
public WindowsSerialPortDevice(
string deviceId,
int baudRate = 9600,
StopBits stopBits = StopBits.One,
Parity parity = Parity.None,
byte byteSize = 8,
ushort readBufferSize = 1024,
ILoggerFactory loggerFactory = null,
IApiService apiService = null) : base(
deviceId,
loggerFactory,
(loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<WindowsSerialPortDevice>())
{
ApiService = apiService ?? new ApiService(null);
ConnectedDeviceDefinition = new ConnectedDeviceDefinition(DeviceId, DeviceType.SerialPort);
if ((byteSize == 5 && stopBits == StopBits.Two) || (stopBits == StopBits.OnePointFive && byteSize > 5))
throw new ArgumentException(Messages.ErrorInvalidByteSizeAndStopBitsCombo);
if (byteSize is < 5 or > 8)
throw new ArgumentOutOfRangeException(nameof(byteSize), Messages.ErrorByteSizeMustBeFiveToEight);
if (baudRate is < 110 or > 256000)
throw new ArgumentOutOfRangeException(nameof(baudRate), Messages.ErrorBaudRateInvalid);
if (stopBits == StopBits.None)
throw new ArgumentException(Messages.ErrorMessageStopBitsMustBeSpecified, nameof(stopBits));
ReadBufferSize = readBufferSize;
_BaudRate = baudRate;
_ByteSize = byteSize;
_StopBits = stopBits;
_Parity = parity;
}
#endregion
#region Public Methods
public Task InitializeAsync(CancellationToken cancellationToken = default) => Task.Run(Initialize, cancellationToken);
private uint Write(byte[] data) => data == null ? 0 : ApiService.AWriteFile(_ReadSafeFileHandle, data, data.Length, out var bytesWritten, 0) ? (uint)bytesWritten : 0;
public override Task<uint> WriteAsync(byte[] data, CancellationToken cancellationToken = default)
{
ValidateConnection();
return Task.Run(() =>
{
var bytesWritten = Write(data);
Logger.LogDataTransfer(new Trace(false, data));
return bytesWritten;
}, cancellationToken);
}
public override Task<TransferResult> ReadAsync(CancellationToken cancellationToken = default)
{
ValidateConnection();
return Task.Run(() =>
{
var buffer = new byte[ReadBufferSize];
var bytesRead = Read(buffer);
var transferResult = new TransferResult(buffer, bytesRead);
Logger.LogDataTransfer(new Trace(false, transferResult));
return transferResult;
}, cancellationToken);
}
public override Task Flush(CancellationToken cancellationToken = default)
{
ValidateConnection();
return Task.Run(() => ApiService.APurgeComm(_ReadSafeFileHandle, APICalls.PURGE_RXCLEAR | APICalls.PURGE_TXCLEAR),
cancellationToken);
}
public override void Dispose()
{
if (disposed)
{
Logger.LogWarning(Messages.WarningMessageAlreadyDisposed, DeviceId);
return;
}
disposed = true;
Logger.LogInformation(Messages.InformationMessageDisposingDevice, DeviceId);
if (_ReadSafeFileHandle != null)
{
_ReadSafeFileHandle.Dispose();
_ReadSafeFileHandle = new SafeFileHandle((IntPtr)0, true);
}
base.Dispose();
}
public void Close() => Dispose();
#endregion
#region Private Methods
private void Initialize()
{
_ReadSafeFileHandle = ApiService.CreateReadConnection(DeviceId, FileAccessRights.GenericRead | FileAccessRights.GenericWrite);
if (_ReadSafeFileHandle.IsInvalid) return;
var dcb = new Dcb();
var isSuccess = ApiService.AGetCommState(_ReadSafeFileHandle, ref dcb);
_ = WindowsHelpers.HandleError(isSuccess, Messages.ErrorCouldNotGetCommState, Logger);
dcb.ByteSize = _ByteSize;
dcb.fDtrControl = 1;
dcb.BaudRate = (uint)_BaudRate;
dcb.fBinary = 1;
dcb.fTXContinueOnXoff = 0;
dcb.fAbortOnError = 0;
dcb.fParity = 1;
#pragma warning disable IDE0010 // Add missing cases
dcb.Parity = _Parity switch
{
Parity.Even => 2,
Parity.Mark => 3,
Parity.Odd => 1,
Parity.Space => 4,
Parity.None => 0,
_ => 0
};
dcb.StopBits = _StopBits switch
{
StopBits.One => 0,
StopBits.OnePointFive => 1,
StopBits.Two => 2,
StopBits.None => throw new ArgumentException(Messages.ErrorMessageStopBitsMustBeSpecified),
_ => throw new ArgumentException(Messages.ErrorMessageStopBitsMustBeSpecified),
};
#pragma warning restore IDE0010 // Add missing cases
isSuccess = ApiService.ASetCommState(_ReadSafeFileHandle, ref dcb);
_ = WindowsHelpers.HandleError(isSuccess, Messages.ErrorCouldNotSetCommState, Logger);
var timeouts = new CommTimeouts
{
WriteTotalTimeoutConstant = 0,
ReadIntervalTimeout = 1,
WriteTotalTimeoutMultiplier = 0,
ReadTotalTimeoutMultiplier = 0,
ReadTotalTimeoutConstant = 0
};
isSuccess = ApiService.ASetCommTimeouts(_ReadSafeFileHandle, ref timeouts);
_ = WindowsHelpers.HandleError(isSuccess, Messages.ErrorCouldNotSetCommTimeout, Logger);
Logger.LogInformation("Serial Port device initialized successfully. Port: {port}", DeviceId);
}
private uint Read(byte[] data)
=>
ApiService.AReadFile(_ReadSafeFileHandle, data, data.Length, out var bytesRead, 0)
? bytesRead
: throw new IOException(Messages.ErrorMessageRead);
private void ValidateConnection()
{
if (!IsInitialized)
{
throw new InvalidOperationException(Messages.ErrorMessageNotInitialized);
}
}
#endregion
}
}

Related

How to get port a process is listening on in .net?

As several blogposts and Stackoverflow answers suggest, it is trivial to get this information via a combination of get-process and Get-NetTCPConnection commandlets. It is possible to execute these commands via .net code, parse the output and retrieve the information.
Is it not possible to get the port number a process is listening on in pure .net code using just the .net libraries? System.Diagnostics.Process.GetProcesByName returns a ton of information via the Process object, except for the port the process is listening on.
Any leads highly appreciated.
Unfortunately, IPGlobalProperties.GetIPGlobalProperties() does not return any information on which process is holding the socket, as it uses GetTcpTable not GetTcpTable2.
You would need to code it yourself. The below code works for TCP over IPv4. You would need similar code for UDP and IPv6.
[DllImport("Iphlpapi.dll", ExactSpelling = true)]
static extern int GetTcpTable2(
IntPtr TcpTable,
ref int SizePointer,
bool Order
);
[StructLayout(LayoutKind.Sequential)]
struct MIB_TCPTABLE
{
public int dwNumEntries;
}
[StructLayout(LayoutKind.Sequential)]
struct MIB_TCPROW2
{
public MIB_TCP_STATE dwState;
public int dwLocalAddr;
public byte localPort1;
public byte localPort2;
// Ports are only 16 bit values (in network WORD order, 3,4,1,2).
// There are reports where the high order bytes have garbage in them.
public byte ignoreLocalPort3;
public byte ignoreLocalPort4;
public int dwRemoteAddr;
public byte remotePort1;
public byte remotePort2;
// Ports are only 16 bit values (in network WORD order, 3,4,1,2).
// There are reports where the high order bytes have garbage in them.
public byte ignoreremotePort3;
public byte ignoreremotePort4;
public int dwOwningPid;
public TCP_CONNECTION_OFFLOAD_STATE dwOffloadState;
}
public enum MIB_TCP_STATE
{
Closed = 1,
Listen,
SynSent,
SynRcvd,
Established,
FinWait1,
FinWait2,
CloseWait,
Closing,
LastAck,
TimeWait,
DeleteTcb
}
enum TCP_CONNECTION_OFFLOAD_STATE
{
TcpConnectionOffloadStateInHost,
TcpConnectionOffloadStateOffloading,
TcpConnectionOffloadStateOffloaded,
TcpConnectionOffloadStateUploading,
TcpConnectionOffloadStateMax
}
static List<IPEndPoint> GetSocketsForProcess(int pid, MIB_TCP_STATE state = MIB_TCP_STATE.Established)
{
const int ERROR_INSUFFICIENT_BUFFER = 0x7A;
var size = 0;
var result = GetTcpTable2(IntPtr.Zero, ref size, false);
if (result != ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception(result);
var ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(size);
result = GetTcpTable2(ptr, ref size, false);
if (result != 0)
throw new Win32Exception(result);
var list = new List<IPEndPoint>();
var count = Marshal.ReadInt32(ptr);
var curPtr = ptr + Marshal.SizeOf<MIB_TCPTABLE>();
var length = Marshal.SizeOf<MIB_TCPROW2>();
for(var i = 0; i < count; i++)
{
var row = Marshal.PtrToStructure<MIB_TCPROW2>(curPtr);
if(row.dwOwningPid == pid && row.dwState == state)
list.Add(new IPEndPoint(row.dwLocalAddr, row.localPort1 << 8 | row.localPort2));
curPtr += length;
}
return list;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
I had similar task not long ago. Here is complete .NET 6 code that you should be able to adopt for your particular needs:
public static int CheckConnection(string[] servers)
{
try
{
var srvs = servers.ToDictionary(k => k.Split("/", StringSplitOptions.RemoveEmptyEntries)[1], v => false);
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] endPoints = ipProperties.GetActiveTcpListeners();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();
var count = 0;
foreach (var info in tcpConnections)
{
var ep = string.Format("{0}:{1}", info.RemoteEndPoint.Address, info.RemoteEndPoint.Port);
if (info.State.ToString().ToUpper() != "ESTABLISHED")
continue;
if (srvs.ContainsKey(ep))
{
count++;
srvs[ep] = true;
}
}
var sb = new StringBuilder();
foreach (var kvp in srvs)
{
sb.AppendFormat("{0,-21}: {1}", kvp.Key, kvp.Value ? "OK" : "FAIL");
sb.AppendLine();
}
Program.logger.Trace($"ZMQF. Connection check:\n{sb}");
return count;
}
catch (Exception ex)
{
Program.logger.Fatal(ex, $"ZMQF. CheckConnection exception. Servers: {(string.Join(",", servers))}");
return -1;
}
}

Serial port: Polling vs Event-Driven approach

I am using Win32 API (Overlapped IO) for serial port communication. I have used PInvoke to call the Win32 API from my C# code.
In my previous implementation, the receiver thread reads data by using Polling mechanism (Infinite while loop reads RX buffer periodically). Now, I am trying to use Event-Driven approach to read the data (Using WaitCommEvent API Call). We expect the new implementation will reduce the CPU usage and Speed-Up the communication.
But, After changing to Event-Driven approach, the communication is actually slowed down, taking twice the time to read when compared to Polling mechanism.
Can anyone suggest which one of these is the best approach improve the communication speed and reduce the CPU usage?
Below is my code snippet:
private void ReadDataForWindows()
{
var data = new byte[255];
// Specify a set of events to be monitored for the port.
if (SetCommMask(this.Handle, EV_DEFAULT))
{
while (this.Handle != (IntPtr)INVALID_HANDLE_VALUE)
{
uint eventMask = 0;
// Wait for an event to occur for the port.
eventMask = this.WaitForEvent();
if (eventMask == EV_RXCHAR)
{
this.ReadFromRXBuffer(data);
}
else if (eventMask == EV_ERR)
{
uint errors;
ClearCommError(this.Handle, out errors, IntPtr.Zero);
}
// Re-specify the set of events to be monitored for the port.
if (!SetCommMask(this.Handle, EV_DEFAULT))
{
break;
}
}
}
}
private void ReadFromRXBuffer(byte[] data)
{
uint dwBytesTransferred;
// Loop for waiting for the data.
do
{
// Read the data from the serial port.
this.Read(data, 255, out dwBytesTransferred);
// Display the data read.
if (dwBytesTransferred > 0)
{
if (this.DataReceived != null)
{
var dataReceived = new byte[dwBytesTransferred];
for (var i = 0; i < dwBytesTransferred; i++)
{
dataReceived[i] = data[i];
}
this.DataReceived(this, new ByteDataReceivedEventArgs(dataReceived, Convert.ToInt32(dwBytesTransferred)));
}
}
} while (dwBytesTransferred > 0);
}
private uint WaitForEvent()
{
uint eventMask = 0;
IntPtr evPtr = Marshal.AllocHGlobal(Marshal.SizeOf(eventMask));
Marshal.WriteInt32(evPtr, 0);
if (WaitCommEvent(this.Handle, evPtr, this.RxOvr.MemPtr) == false)
{
int error = Marshal.GetLastWin32Error();
// Operation is executing in the background
if (error == ERROR_IO_PENDING)
{
this.ReceiveSignal.WaitOne();
}
else
{
this.Fault = "WaitCommEvent() Failed. System Returned Error Code: " +
error.ToString();
return 0;
}
}
eventMask = (uint)Marshal.ReadInt32(evPtr);
return eventMask;
}

Using SCardGetStatusChange to be notified of card insert/remove without polling

I'm trying to detect when a card has been inserted into a reader.
If I do a nasty polling loop like this:
public struct SCARD_READERSTATE
{
[MarshalAs(UnmanagedType.LPWStr)]
public string szReader;
public byte[] pvUserData;
public byte[] rgbAtr;
public uint dwCurrentState;
public uint dwEventState;
public uint cbAtr;
}
byte[] atr = null;
SCARD_READERSTATE[] rs = new SCARD_READERSTATE[1];
rs[0].szReader = readersList[0];
rs[0].dwCurrentState = SCARD_STATE_UNAWARE;
rs[0].dwEventState = SCARD_STATE_PRESENT;
int hctx = hContext.ToInt32();
var cardResult = SCardGetStatusChange(hctx, 100, rs, 1);
if (cardResult == 0 && rs[0].cbAtr > 0 && rs[0].rgbAtr != null)
{
atr = new byte[rs[0].cbAtr];
Array.Copy(rs[0].rgbAtr, atr, rs[0].cbAtr);
}
while ( (rs[0].dwCurrentState & SCARD_STATE_PRESENT) == 0)
{
rs = new SCARD_READERSTATE[1];
rs[0].szReader = readersList[0];
//rs[0].dwCurrentState = SCARD_STATE_PRESENT;
//rs[0].dwEventState = SCARD_STATE_PRESENT;
SCardGetStatusChange(hctx, 100000000, rs, 1);
System.Threading.Thread.Sleep(1000);
}
it works, but it has a nasty thread sleep in it. Ideally I'd like to make a blocking call to SCardGetStatusChange on a background thread and then surface up the events.
Apparently by setting the szReader to the value "\\?PnP?\Notification" it should block, as long as everything else in the struct is 0.
I've changed the code to
rs[0].szReader = "\\\\?PnP?\\Notification";
rs[0].cbAtr = 0;
rs[0].dwCurrentState = 0;
rs[0].dwEventState = 0;
rs[0].pvUserData = new byte[0];
rs[0].rgbAtr = new byte0];
SCardGetStatusChange(hctx, 100000000, rs, 1);
but it just returns a success result immediately. Can any pInvoke masters out there see what's wrong?
In your sample the second call to SCardGetStatusChange should block if you copy dwEventState into dwCurrentState and then reset dwEventState, so there's no need for the sleep.
The "\\?PnP?\Notification" struct is to tell you when a new smart card reader has been attached, not when a card has been inserted. From the MSDN page on SCardGetStatusChange:
To be notified of the arrival of a new smart card reader, set the szReader member of a SCARD_READERSTATE structure to "\\?PnP?\Notification", and set all of the other members of that structure to zero.
When using the "\\?PnP?\Notification" struct:
the pvUserData and rgbAttr fields should be set to null
a new byte[0] is a valid pointer to a zero length array, but what the API needs here is null pointers or zero values)
the high 16 bits of dwCurrentState should contain the current reader count
i.e. rs[0].dwCurrentState = (readerCount << 16);
the MSDN page is currently inaccurate on this point.

Sockets in C++ and C# under Windows not working properly

I'm using UDP sockets to communicate a C++ application with a C# application.
In the C# -> C++ direction everything seems to be working fine, but the other way around is the one that's driving me nuts.
Communication does work, but messages are getting way late (like 2 secs delay) in the C# app, even though they're being sent every frame (it's a 3D app), and the receiving code is executing every 10 ms.
I need real time so this is a very painful problem. Do you think this might be related to packet losses? Then why don't they get lost in the other direction?
EDIT:
C# app code for syncing data:
public void RecibeDatos()
{
if (MessageReceived && U != null && S != null)
{
MessageReceived = false;
//Console.WriteLine("listening for messages");
U.BeginReceive(ReceiveCallback, S);
}
}
public void ReceiveCallback(IAsyncResult ar)
{
UdpClient u = ((UdpState)(ar.AsyncState)).U;
IPEndPoint e = ((UdpState)(ar.AsyncState)).E;
receivedBytes = u.EndReceive(ar, ref e);
//int currentProtocol = (int) numero;
//ResetSignal = reset > 0;
//Console.WriteLine("Received: " + currentProtocol);
MessageReceived = true;
}
C++ Code for sending data:
float indiceFloat[1];
indiceFloat[0] = indice_protocolo_actual;
sender->setBuffer((void *)indiceFloat, sizeof(indiceFloat));
sender->sync();
sync method on J_Enviar (sender) class:
void J_Enviar::sync( void )
{
if(!_initialized) init();
if( _buffer == 0L )
{
fprintf( stderr, "Broadcaster::sync() - No buffer\n" );
return;
}
#if defined (WIN32) && !defined(__CYGWIN__)
unsigned int size = sizeof( SOCKADDR_IN );
sendto( _so, (const char *)_buffer, _buffer_size, 0, (struct sockaddr *)&saddr, size );
int err = WSAGetLastError ();
if (err!=0)
fprintf( stderr, "Broadcaster::sync() - error %d\n",err );
#else
unsigned int size = sizeof( struct sockaddr_in );
sendto( _so, (const void *)_buffer, _buffer_size, 0, (struct sockaddr *)&saddr, size );
#endif
}
Providing full SocketManager code for Receiving C# end:
using System;
using System.Net;
using System.Net.Sockets;
namespace WpfApplication1
{
public class SocketManager
{
private static SocketManager _instance = null;
static readonly object Padlock = new object();
private IPEndPoint E;
private UdpClient U;
private UdpState S;
private byte[] receivedBytes;
public static bool MessageReceived = true;
private SocketManager()
{
}
public byte[] ReceivedBytes
{
get { return receivedBytes; }
}
public static SocketManager Instance
{
get
{
lock(Padlock)
{
return _instance ?? (_instance = new SocketManager());
}
}
}
public void CreateReceivingSocket(IPAddress a, int puerto)
{
if(E==null || (E.Address != a && E.Port != puerto))
{
E = new IPEndPoint(a, puerto);
U = new UdpClient(puerto);
S = new UdpState { E = E, U = U };
}
}
public void ReceiveCallback(IAsyncResult ar)
{
UdpClient u = ((UdpState)(ar.AsyncState)).U;
IPEndPoint e = ((UdpState)(ar.AsyncState)).E;
receivedBytes = u.EndReceive(ar, ref e);
//int currentProtocol = (int) numero;
//ResetSignal = reset > 0;
//Console.WriteLine("Received: " + currentProtocol);
MessageReceived = true;
}
public void RecibeDatos()
{
if (MessageReceived && U != null && S != null)
{
MessageReceived = false;
//Console.WriteLine("listening for messages");
U.BeginReceive(ReceiveCallback, S);
}
}
public void CloseConnection()
{
if (E != null)
{
E.Port = 5502;
E = null;
}
if (U != null)
U.Close();
}
}
public class UdpState
{
public IPEndPoint E;
public UdpClient U;
}
}
And this is my dispatchertimerclick which makes the program receive each 10 ms:
_dispatcherTimer.Tick += DispatcherTimerTick;
_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
_dispatcherTimer.Start();
private void DispatcherTimerTick(object sender, EventArgs e)
{
_exp1Class.sendData();
_sm.RecibeDatos();
byte[] recibidos = _sm.ReceivedBytes;
if (recibidos != null)
{
float numero = BitConverter.ToSingle(recibidos, 0);
_exp1Class.CurrentProtocol = (int) numero;
}
}
I don't see when you kick off your first BeginReceive. (Ah, it is done from your first timer tick I think?) It should be initaited as soon as you are ready to receive data. Secondly, your ReceiveCallback should take the received data and place it into a queue of some sort and immediately call BeginReceive again. Otherwise you are holding up thge arrival of the next data frame until the prior was consumed. Finally, watch for threading issues, as the Threading timer and the UDP callback each will run on seperate threads from your application main thread.
The only reason your code works at all is because you pre-initialized MessageReceived = true even before you receive any callbacks. When the first tick happens the call to RecibeDatos invokes BeginReceive because that bool was set to true.
Think of BeginReceive as saying "call me back when you have some data from the network". You don't need to poll the network using your timer. (You can choose to consume that data via your timer if your application requires that, but lets leave that aside for the moment).
Here are the rough steps:
First, upon start (or enable, run, etc) you should call BeginReceive. You will now receive a notification when data arrives.
Secondly, when the callback happens you complete that read of the data bytes using EndReceive. That data would typically be buffered or otherwise dispatched. You should then call BeginReceive again (within the callback) so that upon the next set of data being available you will again get notified. It becomes a kind of async loop.
The question is what to do with the data you are reading. You might consider placing the data into a queue, and then having your Timer pop these data frames off of the queue for processing. Be aware that the data can arrive multiple times between your Ticks.

Network traffic encryption

I have a proxy program which resend network traffic. It listens specific port, and addresses server with another port. It works, but I need to add some encryption between 2 instances of my proxy. When encryption is not enabled two sequential proxy work good, but with encryption don't work. Here is code:
public class CommunicationContext
{
/// <summary>Define buffer max size. Influence on memory usage.</summary>
public const int MaxBuffer = 16 * 1024;
/// <summary>Unique counter...</summary>
private static int _uniqueCounter = 0;
public readonly Socket SocketIn;
public readonly Socket SocketOut;
public readonly PortMapping Mapping;
public byte[] BufferIn;
public byte[] BufferOut;
public bool IsShutdown;
public readonly object SyncObject = new object();
public readonly int UniqueId;
public CommunicationContext(Socket socketIn, Socket socketOut, PortMapping map){
SocketIn = socketIn;
SocketOut = socketOut;
Mapping = map;
UniqueId = Interlocked.Increment( ref _uniqueCounter );
}
public void InitializeBuffers(){
this.BufferIn = new byte[MaxBuffer];
this.BufferOut = new byte[MaxBuffer];
}
}
private static void ReadInputSocket( IAsyncResult ar ){
CommunicationContext context = (CommunicationContext)ar.AsyncState;
try{
int length = context.SocketIn.EndReceive( ar );
if( length <= 0 )
throw new NoDataSocketException();
lock( context.SyncObject ){
Switches.GeneralLog.Verbose( "==> Client data size: " + length );
SocketFlags flags = ( context.SocketIn.Available == 0 ) ? SocketFlags.None : SocketFlags.Partial;
if(!CryptoTools.CryptEnabled){
//without encrypion works fine
}
else if(CryptoTools.CryptInner){
context.BufferIn = CryptoTools.Crypt(context.BufferIn);
}else{
context.BufferIn = CryptoTools.Decrypt(context.BufferIn);
}
context.SocketOut.Send(context.BufferIn, 0, length, flags);
}
Thread.Sleep( 0 );
context.SocketIn.BeginReceive(context.BufferIn, 0, MaxBuffer, SocketFlags.None, ReadInputSocket, context);
}
catch( Exception ex ){
Switches.GeneralLog.Verbose( ex );
Switches.GeneralLog.Info( ex.Message );
ShutdownCommunication( context );
}
}
private static void ReadOutputSocket(IAsyncResult ar ){
CommunicationContext context = (CommunicationContext)ar.AsyncState;
try{
int length = context.SocketOut.EndReceive( ar);
if( length <= 0 )
throw new NoDataSocketException();
lock( context.SyncObject )
{
Switches.GeneralLog.Verbose( "<== Server data size: " + length );
SocketFlags flags = ( context.SocketOut.Available == 0 ) ? SocketFlags.None : SocketFlags.Partial;
if (!CryptoTools.CryptEnabled){
//without encrypion works fine
}
else if (CryptoTools.CryptInner){
context.BufferOut = CryptoTools.Decrypt(context.BufferOut);
}
else{
context.BufferOut = CryptoTools.Crypt(context.BufferOut);
}
context.SocketIn.Send(context.BufferOut, 0, length, flags);
}
context.SocketOut.BeginReceive(context.BufferOut, 0, MaxBuffer, SocketFlags.None, ReadOutputSocket, context);
}
catch( Exception ex )
{
Switches.GeneralLog.Verbose( ex );
Switches.GeneralLog.Info( ex.Message );
ShutdownCommunication( context );
}
}
Edit from comments:
What not works: Data keep getting corrupted.
I get no exceptions. Just malformed data. I used different methods for Crypt/Decrypt. I made them even both equal - simple XOR. Encryption algorithm has not significance, for example, XOR was used.
My configuration is like this Client <--> Proxy1 <--enc--> Proxy2 <---> Server. Between two proxies must be the encrypted stream.

Categories

Resources