I've a sample pascal program, provided in a technical handbook of coin counter S350/360 machine, that enables communication between the machine and a computer using RS232C cable. I've came up with the following equivalent C# code and for some reason only the procedures that only need to write data to the serial port are executing and working successfully. I can only write data to the port but cannot read any data from it. I checked this using a Serial Sniffer and it turns out it only sniffs data written to the port, using the C# program, but cannot read any data back. Could you guys help me with what's going on here?
First here is the original Pascal code of the sample program.
uses crt;
const
{ COM1: RS232 port address }
RXTX = $3F8; { $2F8 if COM2: is used }
ACK = 6;
NAK = 21;
ESC = 27;
var
dummy,
checkSum : integer;
key : char;
protocol : integer;
procedure InitComm;
{ Set baudrate to 9600, 8 bits, no parity, 1 stop bit }
var i : integer;
begin
i := 1843200 div 9600 div 16;
port[RXTX + 3] := $80;
port[RXTX + 1] := hi(i);
port[RXTX]:= lo(i);
port[RXTX + 3] := 3;
port[RXTX + 4] := $A;
while odd(port[RXTX + 5]) do
begin
dummy := port[RXTX];
delay(10);
end;
end; { InitComm }
procedure Tx(data : integer);
{ Transmit a character on serial channel }
begin
while port[RXTX + 5] and $20 = 0 do;
port[RXTX] := data and $FF;
end; { Tx }
function RxWait : integer;
{ Waits for a character from serial channel }
begin
while not odd(port[RXTX + 5]) do;
RxWait := port[RXTX];
end; { RxWait }
procedure Tx2(data : integer);
{ Transmit a char on serial channel + Calculate check sum }
begin
Tx(data);
checkSum := (checkSum + data) and $FF;
end; { Tx2 }
procedure TxCommand(c1, c2 : char;
sendCheckSum : boolean);
{ Transmit command (no data) on serial channel }
begin
Tx(ESC);
checkSum := 0;
Tx2(ord(c1));
Tx2(ord(c2));
if sendCheckSum then
begin
Tx2(checkSum);
dummy := RxWait;
end;
end; { TxCommand }
function ReadNumber(n : integer) : real;
{ Read n bytes from serial channel }
var
number: real;
i : integer;
begin
number := 0;
checkSum := 0;
for i := 1 to n do
number := number * 256 + RxWait;
dummy := RxWait;
ReadNumber := number;
end; { ReadNumber }
procedure Revisions;
var
tmp : integer;
sw,
prot : real;
begin
TxCommand('P', 'R', FALSE);
checkSum := 0;
tmp := RxWait;
sw := tmp + RxWait / 100.0;
protocol := RxWait;
prot := protocol + RxWait / 100.0;
dummy := RxWait;
tmp := RxWait;
writeln('Software revision: ', sw:4:2);
writeln('Protocol revision: ', prot:4:2);
end; { Revisions }
procedure ReadCountReg;
begin
TxCommand('R', 'C', FALSE);
writeln(ReadNumber(4):11:0, ' coins counted.');
dummy := RxWait;
end; { ReadCountReg }
procedure ReadAccReg;
begin
TxCommand('R', 'A', FALSE);
writeln(ReadNumber(4):11:0, ' coins in accumulator.');
dummy := RxWait;
end; { ReadAccReg }
procedure Setbatch(limit : longint);
begin
TxCommand('W', 'L', FALSE);
case protocol of
1 : begin
Tx2(limit div 256);
Tx2(limit mod 256);
end;
2 : begin
Tx2( limit div 16777216);
Tx2((limit div 65536) mod 256);
Tx2((limit div 256) mod 256);
Tx2( limit mod 256);
end;
end; { case protocol }
Tx2(checkSum);
dummy := RxWait;
end; { Setbatch }
In the protocol descriptions two logical operators are used,
+ for arithmetic addition and ^ for logical AND.
All messages have the following principal structure:
Computer SC 350/360
–––––––> ESC (message start)
–––––––> Command
<––––––> Data (direction depends on command)
<––––––> Check sum (direction depends on command)
<––––––– Receipt:
- ACK (if check sum is correct) or
- NAK (if check sum is incorrect)
If the data is a binary number, the most significant byte (character) is
sent first. If the data is a text string, the first character of the string is
sent first. A text string always ends with a NULL character.
The check sum for received characters in a message is derived as
follows:
The addition of every hexadecimal character value, except those for
the message start (ESC), forms the sum. The check sum is then the
sum ^255.
If any of the fault conditions listed below occurs, the computer
should wait for at least 250 ms from the time that the last character
was received before re-sending the message:
The computer receives an incorrect check sum.
The machine has sent NAK.
The machine sends “invalid data”.
The machine does not respond within 250 ms.
The machine accepts all messages when counting except the message
that sets a new batch quantity.
For example; To start the machine 'TxCommand('M', '1', TRUE);' is issued as written in the handbook; I've implemented the pascal code for this in C#.Net and have successfully issued this command and the machine started right away. What I couldn't get working was the procedures 'ReadCountReg', 'ReadCountReg'and 'Setbatch'.
Below is the C# code I implemented:
public class CoinCounter : IDisposable
{
private const int RXTX = 0x3F8;
private const int ACK = 6;
private const int NAK = 21;
private const int ESC = 27;
private int dummy, checkSum;
private char key;
private int protocol;
public SerialPort port;
private bool _disposed = false;
public CoinCounter()
{
//Initialize Communication
port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
port.Handshake = Handshake.None;
port.Open();
port.DtrEnable = true;
port.RtsEnable = true;
}
public void ClosePort()
{
if (port.IsOpen)
port.Close();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
DisposeManagedResources();
}
DisposeUnmanagedResources();
_disposed = true;
}
}
private void DisposeManagedResources()
{
port.Dispose();
}
private void DisposeUnmanagedResources() { }
~CoinCounter()
{
Dispose(false);
}
public void Tx(int data)
{
port.Write(new byte[] { (byte)(data & 0xFF) }, 0, 1);
}
public int RxWait()
{
int readByte = 0;
CommTimer timer = new CommTimer();
timer.Start(250);
while ((port.BytesToRead == 0) && (timer.timedout == false))
{
}
if (port.BytesToRead > 0)
readByte = port.ReadByte();
return readByte;
}
public void Tx2(int data)
{
Tx(data);
checkSum = (checkSum + data) & 0xFF;
}
public void TxCommand(char c1, char c2, bool sendCheckSum)
{
Tx(ESC);
checkSum = 0;
Tx2((int)c1);
Tx2((int)c2);
if (sendCheckSum)
{
Tx2(checkSum);
dummy = RxWait();
}
}
public double ReadNumber(int n)
{
double number;
int i;
number = checkSum = 0;
for (i = 0; i < n; i++)
number = number * 256 + RxWait();
dummy = RxWait();
return number;
}
public void Revisions(out double softVersion, out double protocolVersion)
{
int tmp;
TxCommand('P', 'R', false);
checkSum = 0;
tmp = RxWait();
softVersion = tmp + RxWait() / 100.0;
protocol = RxWait();
protocolVersion = protocol + RxWait() / 100.0;
dummy = RxWait();
tmp = RxWait();
}
public double ReadCountReg()
{
TxCommand('R', 'C', false);
double coinsCounted = ReadNumber(4);
dummy = RxWait();
return coinsCounted;
}
public double ReadAccReg()
{
TxCommand('R', 'A', false);
double coinsInAccumulator = ReadNumber(4);
dummy = RxWait();
return coinsInAccumulator;
}
public void SetBatch(long limit)
{
TxCommand('W', 'L', false);
switch (protocol)
{
case 1:
Tx2((int)(limit / 256));
break;
case 2:
Tx2((int)(limit / 16777216));
Tx2((int)(limit / 65536) % 256);
Tx2((int)(limit / 256) % 256);
Tx2((int)(limit % 256));
break;
}
Tx2(checkSum);
dummy = RxWait();
}
public class CommTimer
{
public System.Timers.Timer tmrComm = new System.Timers.Timer();
public bool timedout = false;
public CommTimer()
{
timedout = false;
tmrComm.AutoReset = false;
tmrComm.Enabled = false;
tmrComm.Interval = 1000; //default to 1 second
tmrComm.Elapsed += new ElapsedEventHandler(OnTimedCommEvent);
}
public void OnTimedCommEvent(object source, ElapsedEventArgs e)
{
timedout = true;
tmrComm.Stop();
}
public void Start(double timeoutperiod)
{
tmrComm.Interval = timeoutperiod;
tmrComm.Stop();
timedout = false;
tmrComm.Start();
}
}
}
Note that the hand book states:
1. For simplicity the handshake lines are not used in this example.
2. No check sum implemented.
While most of the commands, with simple checksum calculation, can successfully communicate with the machine I couldn't get, for example, 'ReadCountReg' procedure to work and see the results of counted coins. The protocol for this procedure is;
Computer SC 350/360
–––––––> ESC
–––––––> “R”
–––––––> “C”
<––––––– CountRegister (CR)
<––––––– (CR ^ FF00,000016 + CR ^ FF,000016 + CR ^ FF0016 +CR ^ FF16) ^ FF16
<––––––– ACK
Could you guys help me get this working as I don't have much experience with the serial port communication and could help me resolve my issue.
SerialPort implements IDisposable. You are acquiring a serial port and you are never disposing it - this is an error. If your usage model is one wherein the client code will always make a call to you when it's done, you should Dispose the object then (this doesn't look like your model). Otherwise, you should implement IDisposable yourself (follow the code template. In your case, you will be implementing DisposeManagedResources() and disposing the serial port then) and ensure that your object is Disposed when finished.
A final option is to have the calling code allocate and dispose the serial port and pass it in to the constructor of your code.
Related
I have been working on a conversion from Delphi to C#. This is not normally something I deal with. So, I come here humbly to ask for some help.
Here is what I have so far:
Delphi code:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Console in 'Console.pas';
const
CKEY1 = 11111;
CKEY2 = 22222;
function EncryptStr(const s: WideString; Key: word): String;
var
i: integer;
RStr: RawByteString;
RStrB: TBytes Absolute RStr;
begin
Result := '';
RStr := UTF8Encode(s);
for i := 0 to Length(RStr) - 1 do
begin
RStrB[i] := RStrB[i] xor (Key shr 8);
Key := (RStrB[i] + Key) * CKEY1 + CKEY2;
end;
for i := 0 to Length(RStr) - 1 do
begin
Result := Result + IntToHex(RStrB[i], 2);
end;
end;
function DecryptStr(const s: String; Key: word): String;
var
i, tmpKey: integer;
RStr: RawByteString;
RStrB: TBytes Absolute RStr;
tmpStr: string;
begin
tmpStr := UpperCase(s);
SetLength(RStr, Length(tmpStr) div 2);
i := 1;
try
while (i < Length(tmpStr)) do
begin
RStrB[i div 2] := StrToInt('$' + tmpStr[i] + tmpStr[i + 1]);
Inc(i, 2);
end;
except
Result := '';
Exit;
end;
for i := 0 to Length(RStr) - 1 do
begin
tmpKey := RStrB[i];
RStrB[i] := RStrB[i] xor (Key shr 8);
Key := (tmpKey + Key) * CKEY1 + CKEY2;
end;
Result := UTF8Decode(RStr);
end;
var myEncrypted: string;
begin
try
myEncrypted := EncryptStr('TheTestString', 4444);
WriteLn('Encrypted: '+myEncrypted);
ExitCode := 1;
Console.WaitAnyKeyPressed('Press any key to continue ...');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Part of the reason I am putting this out here is that this code originated here (not mine) delphi-simple-string-encryption.
Here is what I have attempted thus far:
C# code:
private const int CKEY1 = 11111;
private const int CKEY2 = 22222;
public static string EncryptAString(string s, int Key)
{
try
{
var encryptedValue = string.Empty;
// Create a UTF-8 encoding.
UTF8Encoding utf8 = new UTF8Encoding();
// Encode the string.
byte[] RStrB = utf8.GetBytes(s);
for (var i = 0; i <= RStrB.Length - 1; i++)
{
RStrB[i] = Convert.ToByte(RStrB[i] ^ (Key >> 8));
Key = (RStrB[i] + Key) * CKEY1 + CKEY2;
//I have a problem right here. See screen shot for values
}
for (var i = 0; i <= RStrB.Length - 1; i++)
{
encryptedValue = encryptedValue + RStrB[i].ToString("X2");
}
return encryptedValue;
}
catch (Exception)
{
throw;
}
}
Delphi
C#
The C# code ultimately gives this error:
Error:
System.OverflowException: Value was either too large or too small for
an unsigned byte. at System.Convert.ToByte(Int32 value) at
ChronicleConvertText.Program.EncryptAString(String s, Int32 Key) in
C:\Users\todd7\source\repos\ChronicleConvertText\ChronicleConvertText\Program.cs:line
70 at ChronicleConvertText.Program.Main(String[] args) in
C:\Users\todd7\source\repos\ChronicleConvertText\ChronicleConvertText\Program.cs:line
17
In the process of researching this a few things came into focus.
In Delphi the I have this warning: [dcc32 Warning] Project1.dpr(58): W1000 Symbol 'UTF8Decode' is deprecated: 'Use UTF8ToWideString or UTF8ToString'. Bear in mind this is in use existing code. So, I have not looked directly at what if any impact switching to 'UTF8ToWideString or UTF8ToString' would have. My job as this point is to see if I can duplicate it if possible.
Another issue is this: hazards-of-converting-binary-data-to-a-string.
I suspect that this may be my lack of understanding of what is going on in the "xor" in Delphi verses "^" in C#. The main question remains can this be done? and How? Thanks for any assistance.
Your constants CKEY1 and CKEY2 and argument Key have int type. So expression
Key = (RStrB[i] + Key) * CKEY1 + CKEY2;
is calculated using 32-bit values. For example:
(4444 + 84) * 11111 + 22222 = 50 332 830
is close to your shown value, isn't it?
Delphi code uses 16-bit unsigned variables and corresponding arithmetics, C# equivalent for Delphi word is ushort
Here is what I ended up with:
private const short CKEY1 = 11111;
private const short CKEY2 = 22222;
public static string EncryptAString(string s, ushort Key)
{
try
{
var encryptedValue = string.Empty;
// Create a UTF-8 encoding.
UTF8Encoding utf8 = new UTF8Encoding();
// Encode the string.
byte[] RStrB = utf8.GetBytes(s);
for (var i = 0; i <= RStrB.Length - 1; i++)
{
RStrB[i] = Convert.ToByte(RStrB[i] ^ (Key >> 8));
Key = (ushort)(((RStrB[i] + Key) * CKEY1) + CKEY2);
}
for (var i = 0; i <= RStrB.Length - 1; i++)
{
encryptedValue = encryptedValue + RStrB[i].ToString("X2");
}
return encryptedValue;
}
catch (Exception)
{
throw;
}
}
Tested against the Delphi code and it works marvelously.
I have an Arduino that send on serial port some information revealed by analogical pin. Anyway, in the Arduino code (that I can not modify) is used Serial.write() instead of Serial.print() in order to print a buffer of char. As a consequence, if in my C# software I read the information with a "simple" ReadLine(), the data are incomprehensible. How can I read these type of data with C#?
It is the Arduino code:
#include <compat/deprecated.h>
#include <FlexiTimer2.h>
#define TIMER2VAL (1024/256) // 256Hz - frequency
volatile unsigned char myBuff[8];
volatile unsigned char c=0;
volatile unsigned int myRead=0;
volatile unsigned char mych=0;
volatile unsigned char i;
void setup() {
pinMode(9, OUTPUT);
noInterrupts();
myBuff[0] = 0xa5; //START 0
myBuff[1] = 0x5a; //START 1
myBuff[2] = 2; //myInformation
myBuff[3] = 0; //COUNTER
myBuff[4] = 0x02; //CH1 HB
myBuff[5] = 0x00; //CH1 LB
myBuff[6] = 0x02; //CH2 HB
myBuff[7] = 0x00; //CH2 LB
myBuff[8] = 0x01; //END
FlexiTimer2::set(TIMER2VAL, Timer2);
FlexiTimer2::start();
Serial.begin(57600);
interrupts();
}
void Timer2()
{
for(mych=0;mych<2;mych++){
myRead= analogRead(mych);
myBuff[4+mych] = ((unsigned char)((myRead & 0xFF00) >> 8)); // Write HB
myBuff[5+mych] = ((unsigned char)(myRead & 0x00FF)); // Write LB
}
// SEND
for(i=0;i<8;i++){
Serial.write(myBuff[i]);
}
myBuff[3]++;
}
void loop() {
__asm__ __volatile__ ("sleep");
}
And this is the C# method that read from serial port
public void StartRead()
{
msp.Open(); //Open the serial port
while (!t_suspend)
{
i++;
String r = msp.ReadLine();
Console.WriteLine(i + ": " + r);
}
}
EDIT: I would as output an array of string that correspond to the data of Arduino output. If I record everything as an array of byte, I have not the information about start and the end of the array.
I can edit the code as:
public void StartRead()
{
msp.Open(); //Open the serial port
ASCIIEncoding ascii = new ASCIIEncoding();
while (!t_suspend)
{
i++;
int r = msp.ReadByte();
String s = ascii.getString((byte)r); // here there is an error, it require an array byte[] and not a single byte
Console.WriteLine(i + ": " + r);
}
}
How I can have the same Arduino array value (but as a String) in my C# software, considering that the starting value is every time 0xa5 and the end is 0x01.
Arduino sends a telegram of several bytes. You can read it into a byte array:
byte[] telegram = byte[msp.BytesToRead];
msp.Read(telegram, 0, msp.BytesToRead);
To get the data from the byte array you have to interpret the bytes (See example below).
Of course you could create a string from the properties of the Telegram class:
class Telegram {
public Telegram(byte[] tel) {
// Check start bytes ( 0xa5, 0x5a );
Info = tel[2];
Counter = tel[3];
Channel1 = BitConverter.ToInt16(new byte[] { tel[5], tel[4] }, 0); // Switch lo/hi byte
Channel2 = BitConverter.ToInt16(new byte[] { tel[7], tel[6] }, 0);// Switch lo/hi byte
// check tel[8] == 1 for end of telegram
}
public int Info { get; private set; }
public int Counter { get; private set; }
public int Channel1 { get; private set; }
public int Channel2 { get; private set; }
}
I have a working chat -server application in C++ on a raspberry pi that listens for clients and sends messages from one client to the others and vice versa using pthreads in for loops. I am using C# as the clients.
The C# clients are sending and receiving data (single byte) to the RPi server continuously, and logging the times when they send and receive data.
Looking at the logged times, I can see that there is a delay of 100 ms or so between when one client sends and the second one receives the data. This kind of delay is unacceptable for my application. I need to get it under 15 ms consistently.
In my C++ program, the time delay between receiving and sending the byte back to the client is 1-2 ms.
I am not sure if there is a problem in how I have coded the C# clients or the C++ server. I upgraded my kernel with the RT PREEMPT patch but this has not affected the delay times.
If I put in a random delay on the order of seconds before sending a byte to the server in the C# program, then the delay times improve significantly - down to 1-2 ms.
Is there a way to optimize it so even when sending continuously, the delay times are very small? I can post the codes if needed.
**EDIT: Here is the server side code on the RPi.
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <string.h> // memset
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <vector>
#include <string>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <sys/time.h>
#include <sched.h>
#include <time.h>
#include <sys/mman.h>
using namespace std;
int BACKLOG;
#define IP_ADDR "192.168.137.99"
#define PORT "8888"
#define MAXLEN 1
#define MY_PRIORITY (49) /* we use 49 as the PRREMPT_RT use 50
as the priority of kernel tasklets
and interrupt handler by default */
#define MAX_SAFE_STACK (8*1024) /* The maximum stack size which is
guaranteed safe to access without
faulting */
#define NSEC_PER_SEC (1000000000) /* The number of nsecs per sec. */
static unsigned int cli_count = 0;
vector<int> cliarray;
vector<vector<unsigned long long> > data;
pthread_attr_t custom_sched_attr;
int fifo_max_prio, fifo_min_prio;
struct sched_param fifo_param;
void stack_prefault(void) {
unsigned char dummy[MAX_SAFE_STACK];
memset(dummy, 0, MAX_SAFE_STACK);
return;
}
void send_message(char *s, int sock){
int i;
for(i=0;i<BACKLOG;i++){
if(cliarray[i]){
if(cliarray[i] != sock){
send(cliarray[i], s, 1,0);
}
}
}
}
/* Send message to all clients */
void send_message_all(char *s){
int k;
for(k=0;k<BACKLOG;k++){
if(cliarray[k]){
send(cliarray[k], s, 1,0);
}
}
}
void *handle_conn(void *pnewsock)
{
int sock = *(int*)pnewsock;
char client_msg[MAXLEN];
int read_size;
struct timeval tv;
bool looprun = true;
int clientint;
vector<unsigned long long> row(4);
while(looprun ){
read_size = recv(sock, client_msg, 1, 0);
gettimeofday(&tv, NULL);
unsigned long long milliseconds_recv =(unsigned long long)(tv.tv_sec) * 1000 +(unsigned long long)(tv.tv_usec) / 1000;
clientint = int(*client_msg);
client_msg[read_size] = '\0';
/* cout << "length of client message: " << strlen(client_msg) << endl;
cout << "# bytes is : " << read_size << endl;
cout << clientint << " received" << endl;*/
send_message(client_msg,sock);
gettimeofday(&tv, NULL);
unsigned long long milliseconds_sent =(unsigned long long)(tv.tv_sec) * 1000 +(unsigned long long)(tv.tv_usec) / 1000;
row = {clientint, milliseconds_recv, milliseconds_sent, strlen(client_msg)};
data.push_back(row);
if (clientint == 100)
{
looprun = false;
break;
}
}
cout << "exit handle -conn " << endl;
pthread_exit(NULL);
}
int main (int argc, char **argv)
{
struct timespec t;
struct sched_param param;
int interval = 50000; /* 50us*/
/* Declare ourself as a real time task */
param.sched_priority = MY_PRIORITY;
if(sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
perror("sched_setscheduler failed");
exit(-1);
}
/* Lock memory */
if(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
perror("mlockall failed");
exit(-2);
}
/* Pre-fault our stack */
stack_prefault();
int connfd =0, n = 0;
int *new_sock, sock;
struct addrinfo hints, *res;
int reuseaddr = 1; // True
// Get the address info
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(IP_ADDR, PORT, &hints, &res) != 0) {
perror("getaddrinfo");
exit (EXIT_FAILURE);
//return 1;
}
// Create the socket
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock == -1) {
perror("socket");
exit (EXIT_FAILURE);
// return 1;
}
// Enable the socket to reuse the address
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1) {
perror("setsockopt");
::close(sock);
exit (EXIT_FAILURE);
//shutdown(sock,2);
// return 1;
}
// Bind to the address
if (bind(sock, res->ai_addr, res->ai_addrlen) == -1) {
perror("bind");
::close(sock);
exit (EXIT_FAILURE);
//shutdown(sock,2);
//return 0;
}
freeaddrinfo(res);
// Listen
if (listen(sock, BACKLOG) == -1) {
perror("listen");
exit (EXIT_FAILURE);
// return 0;
}
cout << "Enter # clients: " ;
cin >> BACKLOG;
cout << "Enter name of text file (num clients - trial #).txt:" << endl;
string filename;
cin >> filename;
cout << "listening for connections" << endl;
// Main loop
// Main loop
bool running = true;
// Initialize clients
while (running)
{
size_t size = sizeof(struct sockaddr_in);
struct sockaddr_in their_addr;
int clilen = sizeof(their_addr);
int newsock = accept(sock, (struct sockaddr*)&their_addr, &size);
if (newsock == -1)
{
perror("accept");
exit (EXIT_FAILURE);
// return -1;
}
cli_count++;
printf("Got a connection from %s on port %d\n", inet_ntoa(their_addr.sin_addr), htons(their_addr.sin_port));
cliarray.push_back(newsock);
if (cli_count == BACKLOG)
{
cout << "Max clients reached" << endl;
running = false;
break;
}
}
ofstream frout("/home/pi/cplus/"+filename,ios::app);
frout << "recv \t" << "time recv (ms) \t" << "time sent (ms) \t" << "length of msg" << endl;
/* Send message to all clients that server is ready to accept data */
char r = char(cli_count);
char *mesg = &r;
send_message_all(mesg);
cout << "length of mesg: " << strlen(mesg) << endl;
//pthread_t from_ard_t, *ptr;
pthread_attr_init(&custom_sched_attr);
pthread_attr_setinheritsched(&custom_sched_attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&custom_sched_attr, SCHED_FIFO);
fifo_max_prio = sched_get_priority_max(SCHED_FIFO);
fifo_param.sched_priority = fifo_max_prio;
pthread_attr_setschedparam(&custom_sched_attr, &fifo_param);
pthread_t *ptr;
ptr =static_cast<pthread_t*>(malloc(sizeof(pthread_t)*cli_count));
int i;
for (i=0;i<BACKLOG;i++)
{
if (pthread_create(&ptr[i], &custom_sched_attr, handle_conn, (void *)&cliarray[i]) != 0)//was newsock
{
fprintf(stderr, "Failed to create thread\n");
exit (EXIT_FAILURE);
}
}
/*if (pthread_create(&from_ard_t, NULL, from_ard, NULL)!=0)
{
fprintf(stderr, "Failed to create thread\n");
}*/
//pthread_join(from_ard_t, NULL);
cout << "Created threads" << endl;
for(i = 0; i < BACKLOG; i++)
{
pthread_join(ptr[i], NULL);
}
cout << "joined send/recv threads" << endl;
close(sock);
/* array for timestamp data */
int numrows = data.size();
for (int k = 0; k < numrows; k++)
{
for (int j = 0; j < 4; j++)
{
frout << data[k][j] << "\t";
}
frout << endl;
}
}
C# client code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;
//make command line possible to save time info in file
namespace sockclient_cs
{
public class Program
{
private System.Object lockThis = new System.Object();
const int MAXLEN = 1;
public bool recvrun = true;
StringBuilder sb = new StringBuilder();
public NetworkStream stream;
string fnrecv;
string fnsend;
public int clicount;
DateTime centuryBegin = new DateTime(1970, 1, 1);
Random rndseed;
public Program(NetworkStream streamer, int clinum, string pathsend, string pathrecv, Random rand)
{
stream = streamer;
clicount = clinum;
fnrecv = pathrecv;
fnsend = pathsend;
rndseed = rand;
}
public void SendData()
{
int[] numarray = new int[] { 70, 80, 90, 100, 60, 50, 40, 30}; // coressponds to %, A, P, _, d
bool looprun = true;
while (looprun)
{
int rnd1 = rndseed.Next(0, numarray.Length);
byte[] writebyte = new byte[] { BitConverter.GetBytes(numarray[rnd1])[0] };
int delay = rndseed.Next(2000,6000);
Thread.Sleep(delay);
Array.Reverse(writebyte);
stream.Write(writebyte, 0, writebyte.Length);
DateTime currentDate = DateTime.Now;
long elapsedTicks = currentDate.Ticks - centuryBegin.Ticks;
Decimal milliseconds = elapsedTicks / (Decimal)TimeSpan.TicksPerMillisecond;
using (StreamWriter sw = File.AppendText(fnsend))
{
sw.WriteLine(numarray[rnd1] + "\t" + milliseconds + "\n");
}
Console.Write("sent: " + numarray[rnd1] + "\n");
if (numarray[rnd1] == 100)
{
looprun = false;
break;
}
}
}
public void ReceiveData()
{
bool recvrun = true;
int numenders = 0;
while (recvrun)
{
String responseData = String.Empty;
byte[] bb = new byte[1]; //1 byte of data coming in
ASCIIEncoding ascii = new ASCIIEncoding();
int bytes;
bytes = stream.Read(bb, 0, bb.Length);
DateTime currentDate = DateTime.Now;
long elapsedTicks = currentDate.Ticks - centuryBegin.Ticks;
Decimal milliseconds = elapsedTicks / (Decimal)TimeSpan.TicksPerMillisecond;
int numback = BitConverter.ToInt16(new byte[] { bb[0], 0x00 }, 0);
using (StreamWriter sw = File.AppendText(fnrecv))
{
sw.WriteLine(numback + "\t" + milliseconds + "\n");
}
//responseData = ascii.GetString(bb, 0, bytes);
Console.WriteLine("Received: " + bb[0] + "\n");
if (numback == 100)
{
numenders++;
if (numenders == clicount-1)
{
recvrun = false;
break;
}
}
}
Console.Write("Exiting receive");
}
}
public class Simple
{
public static void Main()
{
Console.Write("Enter name of recv data file (ex. cli1recv_1.txt):\n");
string recvfile = Console.ReadLine();
string pathrecv = #"C:\Users\Neha\Documents\Ayaz Research\" + recvfile;
Console.Write("Enter name of send data file (ex. cli4send_1.txt):\n");
string sendfile = Console.ReadLine();
string pathsend = #"C:\Users\Neha\Documents\Ayaz Research\" + sendfile;
using (StreamWriter sw = File.CreateText(pathrecv))
{
sw.WriteLine("Received \t Recv time (ms) \n");
}
using (StreamWriter sw = File.CreateText(pathsend))
{
sw.WriteLine("Sent \t Sent time (ms) \n");
}
//SerialPort Serial1 = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
Random seed = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
try
{
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting...");
tcpclnt.Connect("192.168.137.99", 8888); //address of RPi on arbitrary non privileged port
Console.WriteLine("Connected");
NetworkStream stream = tcpclnt.GetStream();
/*Receive the welcome from server */
String responseData = String.Empty;
Byte[] bb = new byte[2]; //1 byte of data coming in
ASCIIEncoding ascii = new ASCIIEncoding();
int bytes = stream.Read(bb, 0, bb.Length);
int numback = BitConverter.ToInt16(new byte[] { bb[0], 0x00 }, 0);
Console.Write("Received initial message from server: " + bb[0] + "\n");
/*byte[] writebyte = new byte[] { BitConverter.GetBytes(82)[0] };
Console.Write("writebyte length is " + writebyte.Length + "\n");
Array.Reverse(writebyte);
stream.Write(writebyte, 0, writebyte.Length);
bytes = stream.Read(bb, 0,bb.Length);
// convert to string info
Console.Write("reading byte length is " + bb.Length + "\n");
responseData = ascii.GetString(bb, 0, bytes);
Console.WriteLine("bb[0] is: " + bb[0] + "and bb[1] is: " + bb[1] + "\n");
int numback = BitConverter.ToInt16(new byte[] { bb[0], 0x00 }, 0);
Console.WriteLine("Received: " + responseData + "\n");
Console.WriteLine("Received: " + numback + "\n");*/
Program clientObject = new Program(stream,numback,pathsend, pathrecv, seed);
//non loop format - for cppserv
ThreadStart sending = new ThreadStart(clientObject.SendData);
Thread sendThread = new Thread(sending);
sendThread.Start();
ThreadStart receiving = new ThreadStart(clientObject.ReceiveData);
Thread recvThread = new Thread(receiving);
recvThread.Start();
sendThread.Join();
recvThread.Join();
tcpclnt.Close();
}
catch (Exception e)
{
Console.WriteLine("Error...." + e.StackTrace);
}
}
}
}
Here is what Client 2 is sending to Client 1 and the timestamp.
Sent Sent time (ms)
70 1467720189893.1576
80 1467720189912.1587
60 1467720189926.1595
60 1467720189937.1602
50 1467720189949.1608
60 1467720189959.1614
40 1467720189969.162
100 1467720190006.1641
Here is what Client 1 is receiving from Client 2 and the timestamp.
Received Recv time (ms)
70 1467720190016.1647
80 1467720190063.1674
60 1467720190079.1683
60 1467720190109.17
50 1467720190126.171
60 1467720190137.1716
40 1467720190149.1723
100 1467720190161.173
Turn off the Nagle algorithm at the sender
Socket.NoDelay = true
I am using C# to communicate via modbus rs485 rs232 to 2 phase meters that among other log the power voltage.
I have to send data over the bus so that i can receive the readings.
I have connected a normal wire and shorted the send and receive.
The data is recieved and this event is fired:
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] buff = new byte[sp.BytesToRead];
//Read the Serial Buffer
sp.Read(buff, 0, buff.Length);
string data= sp.ReadExisting();
foreach (byte b in buff)
{
AddBuffer(b); //Add byte to buffer
}
}
Then this buffer is sent to another function which is this one:
private void AddBuffer(byte b)
{
buffer.Add(b);
byte[] msg = buffer.ToArray();
//Make sure that the message integrity is correct
if (this.CheckDataIntegrity(msg))
{
if (DataReceived != null)
{
ModbusEventArgs args = new ModbusEventArgs();
GetValue(msg, args);
DataReceived(this, args);
}
buffer.RemoveRange(0, buffer.Count);
}
}
I think that the problem lies at the data integrity check:
public bool CheckDataIntegrity(byte[] data)
{
if (data.Length < 6)
return false;
//Perform a basic CRC check:
byte[] CRC = new byte[2];
GetCRC(data, ref CRC);
if (CRC[0] == data[data.Length - 2] && CRC[1] == data[data.Length - 1])
return true;
else
return false;
}
There is a CRC check and what is strange is that it never becomes true. The CRC calculation:
private void GetCRC(byte[] message, ref byte[] CRC)
{
ushort CRCFull = 0xFFFF;
byte CRCHigh = 0xFF, CRCLow = 0xFF;
char CRCLSB;
for (int i = 0; i < (message.Length) - 2; i++)
{
CRCFull = (ushort)(CRCFull ^ message[i]);
for (int j = 0; j < 8; j++)
{
CRCLSB = (char)(CRCFull & 0x0001);
CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);
if (CRCLSB == 1)
CRCFull = (ushort)(CRCFull ^ 0xA001);
}
}
CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
}
The problem is the use of ReadExisting(). It was not to be used in that manner as the buffer was being filled with useless data from the serial port. This problem was identified by #glace in the comments!
You first need to establish communication with your meters through some existing MODBUS master application like MODPOLL. Then, once you have communication working and having valid replies from your device, then and only then start testing your code. This way you make sure that problem can be only in your code and nothing else.
For example, to connect to two slave devices at the same time RS485 must be used instead of RS232, and this requests different wiring and RS485 to RS232 convertor on PC side.
Having RX and TX connected in RS232 for simulation purpose is not a good idea since each MODBUS message from a master (except broadcast messages) needs a reply which is different from just message echo. Also, each MODBUS message from a master has MODBUS client address embedded in it and only single client should reply to it (MODBUS is single master multiple slaves protocol).
As for a CRC calculation, this might help for MODBUS RTU protocol (ASCII is different):
function mb_CalcCRC16(ptr: pointer to byte; ByteCount: byte): word;
var
crc: word;
b, i, n: byte;
begin
crc := $FFFF;
for i := 0 to ByteCount do
if i = 0 then // device id is 1st byte in message, and it is not in the buffer
b := mb_GetMessageID; // so we have to calculate it and put it as 1st crc byte
else
b := ptr^;
Inc(ptr);
endif;
crc := crc xor word(b);
for n := 1 to 8 do
if (crc and 1) = 1 then
crc := (crc shr 1) xor $A001;
else
crc := crc shr 1;
endif;
endfor;
endfor;
Return(crc);
end;
function mb_CalcCRC: word; // Calculate CRC for message in mb_pdu
begin // this message can be one that is just received, or in a reply we have just composed
Return(mb_CalcCRC16(#mb_pdu[1], mb_GetEndOfData));
end;
That's a quote from a working embedded AVR device with implemented MODBUS RTU slave protocol.
There has lots of example of convert CIDR to ip range. But I want to know how can I use start/end ip address to generate a/some cidr in C#?
for example:
I have start ip address(192.168.0.1) and end ip address(192.168.0.254). So use these two address to generate cidr list {192.168.0.0/31, 192.168.0.2/32}. Is there any C# code example?
CIDR class with static methods to split an IP range into a minimal set of disjoint CIDR ranges, which cover exactly the original IP range.
The split methods (the "real" one working on BigIntegers doing the actual work, and the wrapper for IP addresses and CIDR creation) are at the bottom.
Use with foreach (IPRangeToCidr.CIDR c in IPRangeToCidr.CIDR.split(first, last)) ...
Requires System.Numerics.dll in the references.
using System;
using System.Numerics;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
namespace IPRangeToCidr {
public struct CIDR {
private IPAddress address;
private uint network_length, bits;
public CIDR(IPAddress address, uint network_length) {
this.address = address;
this.network_length = network_length;
this.bits = AddressFamilyBits(address.AddressFamily);
if (network_length > bits) {
throw new ArgumentException("Invalid network length " + network_length + " for " + address.AddressFamily);
}
}
public IPAddress NetworkAddress {
get { return address; }
}
public IPAddress LastAddress {
get { return IPAddressAdd(address, (new BigInteger(1) << (int) HostLength) - 1); }
}
public uint NetworkLength {
get { return network_length; }
}
public uint AddressBits {
get { return bits; }
}
public uint HostLength {
get { return bits - network_length; }
}
override public String ToString() {
return address.ToString() + "/" + NetworkLength.ToString();
}
public String ToShortString() {
if (network_length == bits) return address.ToString();
return address.ToString() + "/" + NetworkLength.ToString();
}
/* static helpers */
public static IPAddress IPAddressAdd(IPAddress address, BigInteger i) {
return IPFromUnsigned(IPToUnsigned(address) + i, address.AddressFamily);
}
public static uint AddressFamilyBits(AddressFamily family) {
switch (family) {
case AddressFamily.InterNetwork:
return 32;
case AddressFamily.InterNetworkV6:
return 128;
default:
throw new ArgumentException("Invalid address family " + family);
}
}
private static BigInteger IPToUnsigned(IPAddress addr) {
/* Need to reverse addr bytes for BigInteger; prefix with 0 byte to force unsigned BigInteger
* read BigInteger bytes as: bytes[n] bytes[n-1] ... bytes[0], address is bytes[0] bytes[1] .. bytes[n] */
byte[] b = addr.GetAddressBytes();
byte[] unsigned = new byte[b.Length + 1];
for (int i = 0; i < b.Length; ++i) {
unsigned[i] = b[(b.Length - 1) - i];
}
unsigned[b.Length] = 0;
return new BigInteger(unsigned);
}
private static byte[] GetUnsignedBytes(BigInteger unsigned, uint bytes) {
/* reverse bytes again. check that now higher bytes are actually used */
if (unsigned.Sign < 0) throw new ArgumentException("argument must be >= 0");
byte[] data = unsigned.ToByteArray();
byte[] result = new byte[bytes];
for (int i = 0; i < bytes && i < data.Length; ++i) {
result[bytes - 1 - i] = data[i];
}
for (uint i = bytes; i < data.Length; ++i) {
if (data[i] != 0) throw new ArgumentException("argument doesn't fit in requested number of bytes");
}
return result;
}
private static IPAddress IPFromUnsigned(BigInteger unsigned, System.Net.Sockets.AddressFamily family) {
/* IPAddress(byte[]) constructor picks family from array size */
switch (family) {
case System.Net.Sockets.AddressFamily.InterNetwork:
return new IPAddress(GetUnsignedBytes(unsigned, 4));
case System.Net.Sockets.AddressFamily.InterNetworkV6:
return new IPAddress(GetUnsignedBytes(unsigned, 16));
default:
throw new ArgumentException("AddressFamily " + family.ToString() + " not supported");
}
}
/* splits set [first..last] of unsigned integers into disjoint slices { x,..., x + 2^k - 1 | x mod 2^k == 0 }
* covering exaclty the given set.
* yields the slices ordered by x as tuples (x, k)
* This code relies on the fact that BigInteger can't overflow; temporary results may need more bits than last is using.
*/
public static IEnumerable<Tuple<BigInteger, uint>> split(BigInteger first, BigInteger last) {
if (first > last) yield break;
if (first < 0) throw new ArgumentException();
last += 1;
/* mask == 1 << len */
BigInteger mask = 1;
uint len = 0;
while (first + mask <= last) {
if ((first & mask) != 0) {
yield return new Tuple<BigInteger, uint>(first, len);
first += mask;
}
mask <<= 1;
++len;
}
while (first < last) {
mask >>= 1;
--len;
if ((last & mask) != 0) {
yield return new Tuple<BigInteger, uint>(first, len);
first += mask;
}
}
}
public static IEnumerable<CIDR> split(IPAddress first, IPAddress last) {
if (first.AddressFamily != last.AddressFamily) {
throw new ArgumentException("AddressFamilies don't match");
}
AddressFamily family = first.AddressFamily;
uint bits = AddressFamilyBits(family); /* split on numbers returns host length, CIDR takes network length */
foreach (Tuple<BigInteger, uint> slice in split(IPToUnsigned(first), IPToUnsigned(last))) {
yield return new CIDR(IPFromUnsigned(slice.Item1, family), bits - slice.Item2);
}
}
}
}
It is difficult to determine the what exactly is being asked here (the CIDR list you give doesn't seem to correspond with the given input addresses), however the following code will allow you to find the smallest single CIDR that contains the specified start and end addresses.
You need to first convert the start and end IP addresses into 32 bit integers (e.g. 192.168.0.1 becomes 0xc0a80001), then apply the following algorithm:
var startAddr = 0xc0a80001; // 192.168.0.1
var endAddr = 0xc0a800fe; // 192.168.0.254
// Determine all bits that are different between the two IPs
var diffs = startAddr ^ endAddr;
// Now count the number of consecutive zero bits starting at the most significant
var bits = 32;
var mask = 0;
while (diffs != 0)
{
// We keep shifting diffs right until it's zero (i.e. we've shifted all the non-zero bits off)
diffs >>= 1;
// Every time we shift, that's one fewer consecutive zero bits in the prefix
bits--;
// Accumulate a mask which will have zeros in the consecutive zeros of the prefix and ones elsewhere
mask = (mask << 1) | 1;
}
// Construct the root of the range by inverting the mask and ANDing it with the start address
var root = startAddr & ~mask;
// Finally, output the range
Console.WriteLine("{0}.{1}.{2}.{3}/{4}", root >> 24, (root >> 16) & 0xff, (root >> 8) & 0xff, root & 0xff, bits);
Running it on the two addresses in your question gives:
192.168.0.0/24
Necromancing.
No, there wasn't, and I don't understand why people keep upvoting wrong answers.
Here's the code for IP-range to CIDR & vice-versa:
// https://dev.maxmind.com/geoip/
// https://stackoverflow.com/questions/461742/how-to-convert-an-ipv4-address-into-a-integer-in-c
public static string IPrange2CIDR(string ip1, string ip2)
{
uint startAddr = IP2num(ip1);
uint endAddr = IP2num(ip2);
// uint startAddr = 0xc0a80001; // 192.168.0.1
// uint endAddr = 0xc0a800fe; // 192.168.0.254
// uint startAddr = System.BitConverter.ToUInt32(System.Net.IPAddress.Parse(ip1).GetAddressBytes(), 0);
// uint endAddr = System.BitConverter.ToUInt32(System.Net.IPAddress.Parse(ip2).GetAddressBytes(), 0);
if (startAddr > endAddr)
{
uint temp = startAddr;
startAddr = endAddr;
endAddr = temp;
}
// uint diff = endAddr - startAddr -1;
// int bits = 32 - (int)System.Math.Ceiling(System.Math.Log10(diff) / System.Math.Log10(2));
// return ip1 + "/" + bits;
uint diffs = startAddr ^ endAddr;
// Now count the number of consecutive zero bits starting at the most significant
int bits = 32;
// int mask = 0;
// We keep shifting diffs right until it's zero (i.e. we've shifted all the non-zero bits off)
while (diffs != 0)
{
diffs >>= 1;
bits--; // Every time we shift, that's one fewer consecutive zero bits in the prefix
// Accumulate a mask which will have zeros in the consecutive zeros of the prefix and ones elsewhere
// mask = (mask << 1) | 1;
}
string res = ip1 + "/" + bits;
System.Console.WriteLine(res);
return res;
}
// https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking
public static void CIDR2IP(string IP)
{
string[] parts = IP.Split('.', '/');
uint ipnum = (System.Convert.ToUInt32(parts[0]) << 24) |
(System.Convert.ToUInt32(parts[1]) << 16) |
(System.Convert.ToUInt32(parts[2]) << 8) |
System.Convert.ToUInt32(parts[3]);
int maskbits = System.Convert.ToInt32(parts[4]);
uint mask = 0xffffffff;
mask <<= (32 - maskbits);
uint ipstart = ipnum & mask;
uint ipend = ipnum | (mask ^ 0xffffffff);
string fromRange = string.Format("{0}.{1}.{2}.{3}", ipstart >> 24, (ipstart >> 16) & 0xff, (ipstart >> 8) & 0xff, ipstart & 0xff);
string toRange = string.Format("{0}.{1}.{2}.{3}", ipend >> 24, (ipend >> 16) & 0xff, (ipend >> 8) & 0xff, ipend & 0xff);
System.Console.WriteLine(fromRange + " - " + toRange);
}
public static uint IP2num(string ip)
{
string[] nums = ip.Split('.');
uint first = System.UInt32.Parse(nums[0]);
uint second = System.UInt32.Parse(nums[1]);
uint third = System.UInt32.Parse(nums[2]);
uint fourth = System.UInt32.Parse(nums[3]);
return (first << 24) | (second << 16) | (third << 8) | (fourth);
}
public static void Test()
{
string IP = "5.39.40.96/27";
// IP = "88.84.128.0/19";
CIDR2IP(IP);
// IPrange2CIDR("88.84.128.0", "88.84.159.255");
IPrange2CIDR("5.39.40.96", "5.39.40.127");
System.Console.WriteLine(System.Environment.NewLine);
System.Console.WriteLine(" --- Press any key to continue --- ");
System.Console.ReadKey();
}
I use this for IpV4, let me know if there is a problem in it.
You can find the extraction source code from the following link:
https://blog.ip2location.com/knowledge-base/how-to-convert-ip-address-range-into-cidr/
using System;
using System.Collections.Generic;
using System.Net;
namespace ConsoleApp
{
public class IPNetwork
{
private readonly long _firstIpAddress;
private readonly long _lastIpAddress;
public static IPNetwork[] FromIpRange(IPAddress firstIpAddress, IPAddress lastIpAddress)
=> FromIpRange(IpAddressToLong(firstIpAddress), IpAddressToLong(lastIpAddress));
public static IPNetwork[] FromIpRange(long firstIpAddress, long lastIpAddress)
{
var result = new List<IPNetwork>();
while (lastIpAddress >= firstIpAddress)
{
byte maxSize = 32;
while (maxSize > 0)
{
long mask = IMask(maxSize - 1);
long maskBase = firstIpAddress & mask;
if (maskBase != firstIpAddress)
break;
maxSize--;
}
double x = Math.Log(lastIpAddress - firstIpAddress + 1) / Math.Log(2);
byte maxDiff = (byte)(32 - Math.Floor(x));
if (maxSize < maxDiff)
{
maxSize = maxDiff;
}
var ipAddress = IpAddressFromLong(firstIpAddress);
result.Add(new IPNetwork(ipAddress, maxSize));
firstIpAddress += (long)Math.Pow(2, 32 - maxSize);
}
return result.ToArray();
}
private static long IMask(int s)
{
return (long)(Math.Pow(2, 32) - Math.Pow(2, 32 - s));
}
public static long IpAddressToLong(IPAddress ipAddress)
{
var bytes = ipAddress.GetAddressBytes();
return ((long)bytes[0] << 24) | ((long)bytes[1] << 16) | ((long)bytes[2] << 8) | bytes[3];
}
public static IPAddress IpAddressFromLong(long ipAddress)
=> new IPAddress((uint)IPAddress.NetworkToHostOrder((int)ipAddress));
public IPNetwork(IPAddress prefix, int prefixLength = 32)
{
if (prefix.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
throw new NotSupportedException("IPv6 is not supported");
Prefix = prefix;
PrefixLength = prefixLength;
var mask = (uint)~(0xFFFFFFFFL >> prefixLength);
_firstIpAddress = IpAddressToLong(Prefix) & mask;
_lastIpAddress = _firstIpAddress | ~mask;
}
public static IPNetwork Parse(string value)
{
try
{
var parts = value.Split('/');
return new IPNetwork(IPAddress.Parse(parts[0]), int.Parse(parts[1]));
}
catch
{
throw new FormatException($"Could not parse IPNetwork from {value}");
}
}
public override string ToString() => $"{Prefix}/{PrefixLength}";
public IPAddress Prefix { get; }
public int PrefixLength { get; }
public IPAddress LastAddress => IpAddressFromLong(_lastIpAddress);
public IPAddress FirstAddress => IpAddressFromLong(_firstIpAddress);
public long Total => _lastIpAddress - _firstIpAddress + 1;
}
}
Usage 1:
var startAddress = IPAddress.Parse("192.168.0.0");
var endAddress = IPAddress.Parse("192.168.0.255");
foreach (var item in IPNetwork.FromIpRange(startAddress, endAddress))
Console.WriteLine(item);
Result
192.168.0.0/24
Usage 2:
var startAddress = IPAddress.Parse("192.168.0.1");
var endAddress = IPAddress.Parse("192.168.0.254");
foreach (var item in IPNetwork.FromIpRange(startAddress, endAddress))
Console.WriteLine(item);
Result:
192.168.0.1/32
192.168.0.2/31
192.168.0.4/30
192.168.0.8/29
192.168.0.16/28
192.168.0.32/27
192.168.0.64/26
192.168.0.128/26
192.168.0.192/27
192.168.0.224/28
192.168.0.240/29
192.168.0.248/30
192.168.0.252/31
192.168.0.254/32
I would recommend the use of IPNetwork Library https://github.com/lduchosal/ipnetwork.
As of version 2, it supports IPv4 and IPv6 as well.
Supernet
IPNetwork network = IPNetwork.Parse("192.168.0.1");
IPNetwork network2 = IPNetwork.Parse("192.168.0.254");
IPNetwork ipnetwork = IPNetwork.Supernet(network, network2);
Console.WriteLine("Network : {0}", ipnetwork.Network);
Console.WriteLine("Netmask : {0}", ipnetwork.Netmask);
Console.WriteLine("Broadcast : {0}", ipnetwork.Broadcast);
Console.WriteLine("FirstUsable : {0}", ipnetwork.FirstUsable);
Console.WriteLine("LastUsable : {0}", ipnetwork.LastUsable);
Console.WriteLine("Usable : {0}", ipnetwork.Usable);
Console.WriteLine("Cidr : {0}", ipnetwork.Cidr);
Output
Network : 192.168.0.0
Netmask : 255.255.255.0
Broadcast : 192.168.0.255
FirstUsable : 192.168.0.1
LastUsable : 192.168.0.254
Usable : 254
Cidr : 24
Have fun !
I found this C code and converted it into C# and it's working now.