Hi i'm newbie in RFID reading. So firstly i downloaded pcsc sharp repository from github. Then i tried to read binary from common rfid tag, it works perfect but the next step was to read data from as i think emulated rfid tag. RFID tag controller is pn71501. From this tag using pcsc sharp i can't read any data excluding ATR and uid. I tried to read this tag using my iPhone and it read it. So what i'm doing wrong?
I also tried to use already done software but it couldn't read it also.
Here is what i get using NFC Tools:
PS Smart Card reader i used is ACS ACR1252
Here is my code:
using System;
using System.Text;
using System.Collections.Generic;
using PCSC;
using PCSC.Iso7816;
namespace Transmit {
public class Program {
public static void Main() {
using (var context = ContextFactory.Instance.Establish(SCardScope.System)) {
var readerNames = context.GetReaders();
if (NoReaderFound(readerNames)) {
Console.WriteLine("You need at least one reader in order to run this example.");
Console.ReadKey();
return;
}
var readerName = ChooseRfidReader(readerNames);
if (readerName == null) {
return;
}
String response = "";
using (var rfidReader = context.ConnectReader(readerName, SCardShareMode.Shared, SCardProtocol.Any)) {
// for (byte i = 0x00; i < 0x47; i++) {
var apdu = new CommandApdu(IsoCase.Case3Extended, rfidReader.Protocol) {
CLA = 0xFF,
Instruction = (InstructionCode)0xB0,
P1 = 0x00,
P2 = 0x00,
Le = 0x10
};
using (rfidReader.Transaction(SCardReaderDisposition.Leave)) {
//Console.WriteLine("Retrieving the UID .... ");
var sendPci = SCardPCI.GetPci(rfidReader.Protocol);
var receivePci = new SCardPCI(); // IO returned protocol control information.
var receiveBuffer = new byte[256];
var command = apdu.ToArray();
var bytesReceived = rfidReader.Transmit(
sendPci, // Protocol Control Information (T0, T1 or Raw)
command, // command APDU
command.Length,
receivePci, // returning Protocol Control Information
receiveBuffer,
receiveBuffer.Length); // data buffer
var responseApdu =
new ResponseApdu(receiveBuffer, bytesReceived, IsoCase.Case3Extended, rfidReader.Protocol);
Console.WriteLine(String.Format("SW1: {0:X2} SW2: {1:X2}", responseApdu.SW1, responseApdu.SW2));
//if(responseApdu.DataSize > 0) {
//response += BitConverter.ToString(responseApdu.GetData()).Replace('-', ' ');
response += responseApdu.DataSize;
// }
}
// }
}
/*String[] devidedResponse = response.Split(' ');
String stillResponse = "";
bool notStarted = true;
int skipBytes = 7;
int onByte = 0;
for(int i = 0; i < devidedResponse.Length; i++) {
if (devidedResponse[i] != "D1" && notStarted) {
continue;
} else if (onByte < skipBytes) {
notStarted = false;
onByte += 1;
continue;
} else if (devidedResponse[i] == "FE") {
break;
}
stillResponse += devidedResponse[i] + " ";
}
String res = stillResponse.Trim();
string asciiCharString = "";
var splitResult = res.Split(' ');
foreach (string hexChar in splitResult) {
var byteChar = int.Parse(hexChar, System.Globalization.NumberStyles.HexNumber);
asciiCharString += (char)byteChar;
}*/
Console.WriteLine(response);
}
Console.WriteLine("\nPress any key to exit.");
Console.ReadKey();
}
private static string ChooseRfidReader(IList<string> readerNames) {
// Show available readers.
Console.WriteLine("Available readers: ");
for (var i = 0; i < readerNames.Count; i++) {
Console.WriteLine($"[{i}] {readerNames[i]}");
}
// Ask the user which one to choose.
Console.Write("Which reader is an RFID reader? ");
var line = Console.ReadLine();
if (int.TryParse(line, out var choice) && choice >= 0 && (choice <= readerNames.Count)) {
return readerNames[choice];
}
Console.WriteLine("An invalid number has been entered.");
Console.ReadKey();
return null;
}
private static bool NoReaderFound(ICollection<string> readerNames) =>
readerNames == null || readerNames.Count < 1;
}
}
I know. I looked it up earlier. Can you read the card with the file explorer?
A hardware device like a UART can be read at three different levels
Read/Write UART directly by finding hardware I/O address
Read/Write through driver. In c# Use Open Serial Port. The driver gets the hardware I/O
Read/Write through an application. The application does 1 and/or 2 above.
You have a working application (number 3) and I do not know if it is using method 1 or 2.
With the card reader I'm trying to make your programming as simple as possible. The easiest method is 3 if you have an API. Next easiest is method 2 which you should be able to use if you installed the vendor driver. You should see device in device manager.
To unlock the card (encryption) you also need to install the certificate than came with card. The file explorer should be able to read/write card.
Related
I'm trying to make an program that send a file to a mobile device encrypted, and the device will get it in another program and decrypt it.
So far I've done the bluetooth connection:
private void Connect()
{
using (SelectBluetoothDeviceDialog bldialog = new SelectBluetoothDeviceDialog())
{
bldialog.ShowAuthenticated = true;
bldialog.ShowRemembered = true;
bldialog.ShowUnknown = true;
if (bldialog.ShowDialog() == System.Windows.Forms.DialogResult.OK )
{
if (bldialog.SelectedDevice == null)
{
System.Windows.Forms.MessageBox.Show("No device selected", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
BluetoothDeviceInfo selecteddevice = bldialog.SelectedDevice;
BluetoothEndPoint remoteEndPoint = new BluetoothEndPoint(selecteddevice.DeviceAddress, BluetoothService.ObexFileTransfer);
client = new BluetoothClient();
client.Connect(remoteEndPoint);
session = new ObexClientSession(client.GetStream(), UInt16.MaxValue);
session.Connect(ObexConstant.Target.FolderBrowsing);
tbConnectionDet.Text = string.Format("Connected to: {0}", selecteddevice.DeviceName);
}
}
}
Also I'm sending the file this way:
private void UploadFiles(string[] files)
{
long size = 0;
List<string> filestoupload = new List<string>();
foreach (string filename in files)
{
if (File.Exists(filename))
{
FileInfo info = new FileInfo(filename);
filestoupload.Add(filename);
size += info.Length;
}
}
using (FileForm upform = new FileForm(new List<string>(filestoupload),
false, session, size, null))
{
upform.ExceptionMethod = ExceptionHandler;
upform.ShowDialog();
lsvExplorer.BeginUpdate();
for (int i = 0; i <= upform.FilesUploaded; i++)
{
ListViewItem temp = new ListViewItem(new string[]{ Path.GetFileName(filestoupload[i]),
FormatSize(new FileInfo(filestoupload[i]).Length, false)},
GetIconIndex(Path.GetExtension(filestoupload[i]), false));
temp.Tag = false;
lsvExplorer.Items.Add(temp);
}
lsvExplorer.EndUpdate();
}
}
I'm using 32feet and BrechamObex libraries .
I have the following questions :
I want to send a public key from my phone to my computer then encrypt a file and send it back to my phone. After I receive the file, I want to decrypt it so I will need another program in order to do this. How can I send information from one program to another using bluetooth?
Whenever I send a file, the phone gets it, but the size it always 0
I am trying to learn C# through trials and errors. I have a server in Python, and the client in C#.
The client is supposed to get data from the server, save it to disk ((which it doesn't)), then continue using that data to communicate until the user decides to exit it.
Unfortunately, it has been raising an Exception Access Violation for quite some time, and provides no helpful information as to why.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Scripting.Hosting;
using System.Threading;
using Rage;
using System.Net.Sockets;
using System.IO;
[assembly: Rage.Attributes.Plugin("LSPDFROnlineClient", Description = "LSPDFR Online Client. Used to connect to LSPDFR Online.", Author = "Thecheater887")]
namespace LSPDFROnlineClient
{
public class EntryPoint
{
private static int IsDead;
public static void Main()
{
IsDead = 0;
Thread mt = new Thread(new ThreadStart(Run));
mt.Start();
while (true)
{
if (IsDead == 1)
{
break;
}
GameFiber.Yield();
}
return;
}
public static void Run()
{
File.WriteAllText("C:/ProgramData/dbg.log","1");
try
{
Byte[] header;
Byte[] packet;
Byte[] data;
Byte[] kad;
Byte[] td;
int paylen;
String rd;
String nd;
String msgid;
File.WriteAllText("C:/ProgramData/dbg.log", "2");
TcpClient connector = new TcpClient();
connector.Connect("127.0.0.1", 5773);
NetworkStream conn = connector.GetStream();
try {
File.WriteAllText("C:/ProgramData/dbg.log", "3");
FileStream savedat = File.OpenRead("C:/Users/Public/Documents/save.dat");
BinaryReader savdat = new BinaryReader(savedat);
nd = savdat.ReadString();
savdat.Close();
if (nd.Length == 16)
{
} else {
File.WriteAllText("C:/ProgramData/save.dat", "user000000000000");
nd = "user000000000000";
}
}
catch
{
File.WriteAllText("C:/ProgramData/save.dat", "user000000000000");
nd = "user000000000000";
}
File.WriteAllText("C:/ProgramData/dbg.log", "4");
data = new Byte[26];
data = Encoding.ASCII.GetBytes("clogr00000" + nd);
conn.Write(data, 0, data.Length);
while (true)
{
try
{
// Get header of packet
header = new Byte[26];
Int32 rcvhead = conn.Read(header, 0, header.Length);
String hd = Encoding.ASCII.GetString(header, 0, rcvhead);
//Deal with it 8-)
msgid = hd.Substring(0, 5);
paylen = Convert.ToInt32(hd.Substring(5, 5));
string servkey = hd.Substring(10, 16);
}
catch
{
continue;
}
File.WriteAllText("C:/ProgramData/dbg.log", "5");
try
{
//Receive packet data
if (paylen > 0)
{
packet = new Byte[paylen];
Int32 newdata = conn.Read(packet, 0, packet.Length);
rd = Encoding.ASCII.GetString(packet, 0, newdata);
}
else
{
rd = null;
}
File.WriteAllText("C:/ProgramData/dbg.log", "6");
if (msgid == "ConOK")
{
File.WriteAllText("C:/ProgramData/dbg.log", "7");
string userid = rd.Substring(0, 16);
Game.DisplayHelp(rd.Substring(16, (rd.Length - 16)));
File.WriteAllText("C:/ProgramData/save.dat", userid);
File.WriteAllText("C:/ProgramData/dbg.log", "8");
}
else if (msgid == "savdt")
{
File.WriteAllText("C:/ProgramData/dbg.log", "9");
string[] tnd = rd.Split(',');
var nud = new List<string>();
nud.Add("Player1");
nud.AddRange(tnd);
File.WriteAllText("C:/ProgramData/dbg.log", "A");
string name = nud[0];
string streetname = nud[1];
int money = Convert.ToInt32(nud[2]);
int bounty = Convert.ToInt32(nud[3]);
int playerrep = Convert.ToInt32(nud[4]);
File.WriteAllText("C:/ProgramData/dbg.log", "B");
int rep = Convert.ToInt32(nud[5]);
string pclass = nud[6];
bool canbecop = Convert.ToBoolean(nud[7]);
int rank = Convert.ToInt32(nud[8]);
int stars = Convert.ToInt32(nud[9]);
int cites = Convert.ToInt32(nud[10]);
File.WriteAllText("C:/ProgramData/dbg.log", "C");
int citesgiven = Convert.ToInt32(nud[11]);
int citesdismissed = Convert.ToInt32(nud[12]);
int arrestsmade = Convert.ToInt32(nud[13]);
int arrested = Convert.ToInt32(nud[14]);
int convictionsmade = Convert.ToInt32(nud[15]);
int convitced = Convert.ToInt32(nud[16]);
string warrant = nud[17];
File.WriteAllText("C:/ProgramData/dbg.log", "D");
int prisontimeremaining = Convert.ToInt32(nud[18]);
int copskilled = Convert.ToInt32(nud[19]);
int crimskilled = Convert.ToInt32(nud[20]);
int civskilled = Convert.ToInt32(nud[21]);
int bountyclaimed = Convert.ToInt32(nud[22]);
int overflowprep = Convert.ToInt32(nud[23]);
string title = nud[24];
bool banned = Convert.ToBoolean(nud[25]);
bool vip = Convert.ToBoolean(nud[26]);
int viprank = Convert.ToInt32(nud[27]);
File.WriteAllText("C:/ProgramData/dbg.log", "E");
var v3 = new Vector3();
float posx = Convert.ToSingle(nud[29]);
float posy = Convert.ToSingle(nud[30]);
float posz = Convert.ToSingle(nud[31]);
v3.X = posx;
v3.Y = posy;
v3.Z = posz;
File.WriteAllText("C:/ProgramData/dbg.log", "EE");
int rot = Convert.ToInt32(nud[32]);
File.WriteAllText("C:/ProgramData/dbg.log", "FF");
World.TeleportLocalPlayer(v3, false);
File.WriteAllText("C:/ProgramData/dbg.log", "F");
string custommessage = nud[28];
if (custommessage == "null")
{
} else {
Game.DisplayNotification(custommessage);
}
}
else if (msgid == "isalv")
{
kad = new Byte[26];
kad = Encoding.ASCII.GetBytes("yesil00000" + nd);
conn.Write(kad, 0, kad.Length);
}
else if (msgid == "pospk")
{
}
else
{
Game.DisplayNotification("Unknown packet recieved! ID: " + msgid);
}
//send end client turn
td = new Byte[26];
td = Encoding.ASCII.GetBytes("endmt00000" + nd);
conn.Write(td, 0,td.Length);
File.WriteAllText("C:/ProgramData/dbg.log", "0");
//
}
catch (Exception e)
{
Game.DisplayHelp(Convert.ToString(e));
Game.DisplayNotification("LSPDFR Online has crashed. Try reloading it maybe..?");
}
}
} catch (Exception e) {
Game.DisplayHelp(Convert.ToString(e));
Game.DisplayNotification("Connection interrupted! Reconnecting....");
IsDead = 1;
return;
}
}
}
}
The protocol goes as such;
Client -> Server: LoginRequest
Server -> Client: LoginOkay
Client -> Server: EndTurnMessage
Server -> Client: SaveDataMessage
Client -> Server: EndTurnMessage
Server -> Client: PositionUpdatePacket
Client -> Server: EndTurnMessage
Then continue routine, however, the server only receives one of these EndTurnMessage packets, which means it is choking on the save data portion, right?
Possibly, but that was working at an earlier time without flaw, and hasn't been modified since.
It is a class file, so it can't be debugged, and I've been tearing my hair out as to what is causing it.
Yes, it is crap-code, and needs rewritten at some point, I am aware, but I'd like it to work before I rewrite it entirely.
TL;DR: Why is this code raising an Access Violation? It's around the savdt sector or after.
UPDATE: I fixed the issue posted in the first answer, however, that didn't do much, so, as posted in both the answer and comments, it's rather difficult to debug with a program, so I'll try the old fashioned route of logging info every so many lines of code. I'll keep this updated.
UPDATE 2: I have figured out from the log debugging, that the line causing the error is World.TeleportLocalPlayer(v3, false);. Unfortunatley, World can't be inherited, and the documentation claims that Vector3 requires you to set it's internal values using get and set. I saw that on MSDN previously, but have no clue about how to search it, and there is nor get or set methods available within that Vector3 object.
You might have a stream that remained open, which prevents a new one to be created. If the msgid is "ConOK" you are creating a new instance without closing it after the write operation is done.
if (msgid == "ConOK"){
string userid = rd.Substring(0, 16);
Game.DisplayHelp(rd.Substring(16, (rd.Length - 16)));
FileStream savedat = File.OpenWrite(("C:/ProgramData/save.dat"));
BinaryWriter savdat = new BinaryWriter(savedat);
savdat.Write(userid);
// Close file stream here
}
But that's just a first guess. You can help us and yourself by making use of the debugger. The fact that your code is contained by a "class file" is no problem but a requirement.
Hava a look at this article for more information about debugging in the world of C#:
http://www.dotnetperls.com/debugging
At a first glance, you can have different class of bugs. Disregarding the logic flows and intended behaviour of the program, let's start with basic debugging.
at this step, don't use threads and fibers, from Main just call Run
there isn't strong input validation
use a lot more of try catch, isolating small pieces of code
in the catch, print ex.Message and ex.StackTrace
read carefully the docs about the methods you call and their possible exceptions
it is weird you write a file inside a exception (catch)
possible race conditions on global variables?
inside Run to set IsDead use Interlocked.Increment
https://msdn.microsoft.com/en-us/library/dd78zt0c(v=vs.110).aspx
...
remove the unused ( I think like Microsoft.Scripting.Hosting ), it only confuse-a-cat
Good day everyone, I would like to ask on how to write in Smartcard. I just rely on the example given on the documentation but it only has read tag.
I follow the examples here in https://github.com/danm-de/pcsc-sharp/blob/master/Examples/Transmit/Program.cs
using System;
using PCSC;
using PCSC.Iso7816;
namespace Transmit
{
public class Program
{
public static void Main() {
using (var context = new SCardContext()) {
context.Establish(SCardScope.System);
var readerNames = context.GetReaders();
if (readerNames == null || readerNames.Length < 1) {
Console.WriteLine("You need at least one reader in order to run this example.");
Console.ReadKey();
return;
}
var readerName = ChooseRfidReader(readerNames);
if (readerName == null) {
return;
}
using (var rfidReader = new SCardReader(context)) {
var sc = rfidReader.Connect(readerName, SCardShareMode.Shared, SCardProtocol.Any);
if (sc != SCardError.Success) {
Console.WriteLine("Could not connect to reader {0}:\n{1}",
readerName,
SCardHelper.StringifyError(sc));
Console.ReadKey();
return;
}
var apdu = new CommandApdu(IsoCase.Case2Short, rfidReader.ActiveProtocol) {
CLA = 0xFF,
Instruction = InstructionCode.GetData,
P1 = 0x00,
P2 = 0x00,
Le = 0 // We don't know the ID tag size
};
sc = rfidReader.BeginTransaction();
if (sc != SCardError.Success) {
Console.WriteLine("Could not begin transaction.");
Console.ReadKey();
return;
}
Console.WriteLine("Retrieving the UID .... ");
var receivePci = new SCardPCI(); // IO returned protocol control information.
var sendPci = SCardPCI.GetPci(rfidReader.ActiveProtocol);
var receiveBuffer = new byte[256];
var command = apdu.ToArray();
sc = rfidReader.Transmit(
sendPci, // Protocol Control Information (T0, T1 or Raw)
command, // command APDU
receivePci, // returning Protocol Control Information
ref receiveBuffer); // data buffer
if (sc != SCardError.Success) {
Console.WriteLine("Error: " + SCardHelper.StringifyError(sc));
}
var responseApdu = new ResponseApdu(receiveBuffer, IsoCase.Case2Short, rfidReader.ActiveProtocol);
Console.Write("SW1: {0:X2}, SW2: {1:X2}\nUid: {2}",
responseApdu.SW1,
responseApdu.SW2,
responseApdu.HasData ? BitConverter.ToString(responseApdu.GetData()) : "No uid received");
rfidReader.EndTransaction(SCardReaderDisposition.Leave);
rfidReader.Disconnect(SCardReaderDisposition.Reset);
Console.ReadKey();
}
}
}
private static string ChooseRfidReader(string[] readerNames) {
// Show available readers.
Console.WriteLine("Available readers: ");
for (var i = 0; i < readerNames.Length; i++) {
Console.WriteLine("[" + i + "] " + readerNames[i]);
}
// Ask the user which one to choose.
Console.Write("Which reader is an RFID reader? ");
var line = Console.ReadLine();
int choice;
if (!(int.TryParse(line, out choice)) || (choice < 0) || (choice > readerNames.Length)) {
Console.WriteLine("An invalid number has been entered.");
Console.ReadKey();
return null;
}
return readerNames[choice];
}
}
}
I read the documentation but I cannot fully understand on how to CommandAdpu of writing data. I will gladly appreciate if someone can provide me a code snippet on how to write in smart card. Thank you very much!
https://danm.de/docs/pcsc-sharp/
Before starting anything, You should read about Mifare card first, can get the document Here.
And then try to communicate with the card by any APDU tool.
You can use pyApduTool to send commands to the cards if you don't have any such tool.
If you have SCM reader then This document will help you to understand about commands need to send on Mifare classic card.
Also check this and search other Mifare topic to learn about Mifare cards. With all this links you will get to know what commands need to send to write/read Mifare cards and once you will know about APDU/Commands to fire, you can build the same in your code as you said you already read mifare with your code. Just replace write command in your code and if everything fine you can write as you looking for.
Hope it helps..
I have to implement a tcp connection where raw xml data is passed.
Unfortunately there is no message framing, I now this is realy bad, but I have to deal with this...
The Message would look like this:
<?xml version="1.0" encoding="utf-8"?>
<DATA></DATA>
or this
<?xml version="1.0" encoding="utf-8"?>
<DATA />
Now I have to receive messages that could have self closed tags. The message is always the same, it is always like xml description and a data tag with inner xml that is the message content.
So if it would be without self closed tags, this would be easy, but how can I read both?
By the way I am using the TcpListener.
Edit :
Everything is fine if there is no self closed tag.
if (_clientSocket != null)
{
NetworkStream networkStream = _clientSocket.GetStream();
_clientSocket.ReceiveTimeout = 100; // 1000 miliseconds
while (_continueProcess)
{
if (networkStream.DataAvailable)
{
bool isMessageComplete = false;
String messageString = String.Empty;
while (!isMessageComplete)
{
var bytes = new byte[_clientSocket.ReceiveBufferSize];
try
{
int bytesReaded = networkStream.Read(bytes, 0, (int) _clientSocket.ReceiveBufferSize);
if (bytesReaded > 0)
{
var data = Encoding.UTF8.GetString(bytes, 0, bytesReaded);
messageString += data;
if (messageString.IndexOf("<DATA", StringComparison.OrdinalIgnoreCase) > 0 &&
messageString.IndexOf("</DATA", StringComparison.OrdinalIgnoreCase) > 0)
{
isMessageComplete = true;
}
}
}
catch (IOException)
{
// Timeout
}
catch (SocketException)
{
Console.WriteLine("Conection is broken!");
break;
}
}
}
Thread.Sleep(200);
} // while ( _continueProcess )
networkStream.Close();
_clientSocket.Close();
}
Edit 2 (30.03.2015 12:00)
Unfortunately it is not possible to use some kind of message frame.
So I ended up to use this part of code (DATA is my root node):
if (_clientSocket != null)
{
NetworkStream networkStream = _clientSocket.GetStream();
_clientSocket.ReceiveTimeout = 100;
string data = string.Empty;
while (_continueProcess)
{
try
{
if (networkStream.DataAvailable)
{
Stopwatch sw = new Stopwatch();
sw.Start();
var bytes = new byte[_clientSocket.ReceiveBufferSize];
int completeXmlLength = 0;
int bytesReaded = networkStream.Read(bytes, 0, (int) _clientSocket.ReceiveBufferSize);
if (bytesReaded > 0)
{
message.AddRange(bytes);
data += Encoding.UTF8.GetString(bytes, 0, bytesReaded);
if (data.IndexOf("<?", StringComparison.Ordinal) == 0)
{
if (data.IndexOf("<DATA", StringComparison.Ordinal) > 0)
{
Int32 rootStartPos = data.IndexOf("<DATA", StringComparison.Ordinal);
completeXmlLength += rootStartPos;
var root = data.Substring(rootStartPos);
int rootCloseTagPos = root.IndexOf(">", StringComparison.Ordinal);
Int32 rootSelfClosedTagPos = root.IndexOf("/>", StringComparison.Ordinal);
// If there is an empty tag that is self closed.
if (rootSelfClosedTagPos > 0)
{
string rootTag = root.Substring(0, rootSelfClosedTagPos +1);
// If there is no '>' between the self closed tag and the start of '<DATA'
// the root element is empty.
if (rootTag.IndexOf(">", StringComparison.Ordinal) <= 0)
{
completeXmlLength += rootSelfClosedTagPos;
string messageXmlString = data.Substring(0, completeXmlLength + 1);
data = data.Substring(messageXmlString.Length);
try
{
// parse complete xml.
XDocument xmlDocument = XDocument.Parse(messageXmlString);
}
catch(Exception)
{
// Invalid Xml.
}
continue;
}
}
if (rootCloseTagPos > 0)
{
Int32 rootEndTagStartPos = root.IndexOf("</DATA", StringComparison.Ordinal);
if (rootEndTagStartPos > 0)
{
var endTagString = root.Substring(rootEndTagStartPos);
completeXmlLength += rootEndTagStartPos;
Int32 completeEndPos = endTagString.IndexOf(">", StringComparison.Ordinal);
if (completeEndPos > 0)
{
completeXmlLength += completeEndPos;
string messageXmlString = data.Substring(0, completeXmlLength + 1);
data = data.Substring(messageXmlString.Length);
try
{
// parse complete xml.
XDocument xmlDocument = XDocument.Parse(messageXmlString);
}
catch(Exception)
{
// Invalid Xml.
}
}
}
}
}
}
}
sw.Stop();
string timeElapsed = sw.Elapsed.ToString();
}
}
catch (IOException)
{
data = String.Empty;
}
catch (SocketException)
{
Console.WriteLine("Conection is broken!");
break;
}
}
This code I had use if ther were some kind of message framing, in this case 4 bytes of message length:
if (_clientSocket != null)
{
NetworkStream networkStream = _clientSocket.GetStream();
_clientSocket.ReceiveTimeout = 100;
string data = string.Empty;
while (_continueProcess)
{
try
{
if (networkStream.DataAvailable)
{
Stopwatch sw = new Stopwatch();
sw.Start();
var lengthBytes = new byte[sizeof (Int32)];
int bytesReaded = networkStream.Read(lengthBytes, 0, sizeof (Int32) - offset);
if (bytesReaded > 0)
{
offset += bytesReaded;
message.AddRange(lengthBytes.Take(bytesReaded));
}
if (offset < sizeof (Int32))
{
continue;
}
Int32 length = BitConverter.ToInt32(message.Take(sizeof(Int32)).ToArray(), 0);
message.Clear();
while (length > 0)
{
Int32 bytesToRead = length < _clientSocket.ReceiveBufferSize ? length : _clientSocket.ReceiveBufferSize;
byte[] messageBytes = new byte[bytesToRead];
bytesReaded = networkStream.Read(messageBytes, 0, bytesToRead);
length = length - bytesReaded;
message.AddRange(messageBytes);
}
try
{
string xml = Encoding.UTF8.GetString(message.ToArray());
XDocument xDocument = XDocument.Parse(xml);
}
catch (Exception ex)
{
// Invalid Xml.
}
sw.Stop();
string timeElapsed = sw.Elapsed.ToString();
}
}
catch (IOException)
{
data = String.Empty;
}
catch (SocketException)
{
Console.WriteLine("Conection is broken!");
break;
}
}
Like you can see I wanted to measure the elapsed time, to see witch methode has a better performance. The strange thing is that the methode whith no message framing has an average time of 0,2290 ms, the other methode has an average time of 1,2253 ms.
Can someone explain me why? I thought the one without message framing would be slower...
Hand the NetworkStream to the .NET XML infrastructure. For example create an XmlReader from the NetworkStream.
Unfortunately I did not find a built-in way to easily create an XmlDocument from an XmlReader that has multiple documents in it. It complains about multiple root elements (which is correct). You would need to wrap the XmlReader and make it stop returning nodes when the first document is done. You can do that by keeping track of some state and by looking at the nesting level. When the nesting level is zero again the first document is done.
This is just a raw sketch. I'm pretty sure this will work and it handles all possible XML documents.
No need for this horrible string processing code that you have there. The existing code looks quite slow as well but since this approach is much better it serves no purpose to comment on the perf issues. You need to throw this away.
I had the same problem - 3rd party system sends messages in XML format via TCP but my TCP client application may receive message partially or several messages at once. One of my colleagues proposed very simple and quite generic solution.
The idea is to have a string buffer which should be populated char by char from TCP stream, after each char try to parse buffer content with regular .Net XML parser. If parser throws an exception - continue adding chars to the buffer. Otherwise - message is ready and can be processed by application.
Here is the code:
private object _dataReceiverLock = new object();
private string _messageBuffer;
private Stopwatch _timeSinceLastMessage = new Stopwatch();
private List<string> NormalizeMessage(string rawMsg)
{
lock (_dataReceiverLock)
{
List<string> result = new List<string>();
//following code prevents buffer to store too old information
if (_timeSinceLastMessage.ElapsedMilliseconds > _settings.ResponseTimeout)
{
_messageBuffer = string.Empty;
}
_timeSinceLastMessage.Restart();
foreach (var ch in rawMsg)
{
_messageBuffer += ch;
if (ch == '>')//to avoid extra checks
{
if (IsValidXml(_messageBuffer))
{
result.Add(_messageBuffer);
_messageBuffer = string.Empty;
}
}
}
return result;
}
}
private bool IsValidXml(string xml)
{
try
{
//fastest way to validate XML format correctness
using (XmlTextReader reader = new XmlTextReader(new StringReader(xml)))
{
while (reader.Read()) { }
}
return true;
}
catch
{
return false;
}
}
Few comments:
Need to control lifetime of string buffer, otherwise in case network disconnection old information may stay in the buffer forever
There major problem here is the performance - parsing after every new character is quite slow. So need to add some optimizations, such as parse only after '>' character.
Make sure this method is thread safe, otherwise several threads may flood string buffer with different XML pieces.
The usage is simple:
private void _tcpClient_DataReceived(byte[] data)
{
var rawMsg = Encoding.Unicode.GetString(data);
var normalizedMessages = NormalizeMessage(rawMsg);
foreach (var normalizedMessage in normalizedMessages)
{
//TODO: your logic
}
}
it is possible using OpenPop.dll.
Pop3Client objPOP3Client = new Pop3Client();
int intTotalEmail = 0;
DataTable dtEmail = new DataTable();
object[] objMessageParts;
try
{
dtEmail = GetAllEmailStructure();
if (objPOP3Client.Connected)
objPOP3Client.Disconnect();
objPOP3Client.Connect(strHostName, intPort, bulUseSSL);
try
{
objPOP3Client.Authenticate(strUserName, new Common()._Decode(strPassword));
intTotalEmail = objPOP3Client.GetMessageCount();
AddMapping();
for (int i = 1; i <= intTotalEmail; i++)
{
objMessageParts = GetMessageContent(i, ref objPOP3Client, dtExistMailList);
if (objMessageParts != null && objMessageParts[0].ToString() == "0")
{
AddToDtEmail(objMessageParts, i, dtEmail, dtUserList, dtTicketIDList, dtBlacklistEmails, dtBlacklistSubject, dtBlacklistDomains);
}
}
}
catch (Exception ex)
{
}
}
catch (Exception ex)
{
ParserLogError(ex, "GetAllEmail()");
}
finally
{
if (objPOP3Client.Connected)
objPOP3Client.Disconnect();
}
// function
public object[] GetMessageContent(int intMessageNumber, ref Pop3Client objPOP3Client, DataTable dtExistingMails)
{
object[] strArrMessage = new object[10];
Message objMessage;
MessagePart plainTextPart = null, HTMLTextPart = null;
string strMessageId = "";
try
{
strArrMessage[0] = "";
strArrMessage[1] = "";
strArrMessage[2] = "";
strArrMessage[3] = "";
strArrMessage[4] = "";
strArrMessage[5] = "";
strArrMessage[6] = "";
strArrMessage[7] = null;
strArrMessage[8] = null;
strArrMessage[7] = "";
strArrMessage[8] = "";
objMessage = objPOP3Client.GetMessage(intMessageNumber);
strMessageId = (objMessage.Headers.MessageId == null ? "" : objMessage.Headers.MessageId.Trim());
if (!IsExistMessageID(dtExistingMails, strMessageId)) //check in data base message id is exists or not
{
strArrMessage[0] = "0";
strArrMessage[1] = objMessage.Headers.From.Address.Trim(); // From EMail Address
strArrMessage[2] = objMessage.Headers.From.DisplayName.Trim(); // From EMail Name
strArrMessage[3] = objMessage.Headers.Subject.Trim();// Mail Subject
plainTextPart = objMessage.FindFirstPlainTextVersion();
strArrMessage[4] = (plainTextPart == null ? "" : plainTextPart.GetBodyAsText().Trim());
HTMLTextPart = objMessage.FindFirstHtmlVersion();
strArrMessage[5] = (HTMLTextPart == null ? "" : HTMLTextPart.GetBodyAsText().Trim());
strArrMessage[6] = strMessageId;
List<MessagePart> attachment = objMessage.FindAllAttachments();
strArrMessage[7] = null;
strArrMessage[8] = null;
if (attachment.Count > 0)
{
if (attachment[0] != null && attachment[0].IsAttachment)
{
strArrMessage[7] = attachment[0].FileName.Trim();
strArrMessage[8] = attachment[0];
}
}
}
else
{
strArrMessage[0] = "1";
}
}
catch (Exception ex)
{
ParserLogError(ex, "GetMessageContent()");
}
return strArrMessage;
}
but, i want to make it faster than above OpenPop.dll. so please let me know if any other technique are there for parsing mails.
please check code and then tell me.
Thanks in advance
but, i want to make it faster than above OpenPop.dll. so please let me
know if any other technique are there for parsing mails.
In your GetMessageContent() method, the 1 place that consumes the vast amount of time is:
objMessage = objPOP3Client.GetMessage(intMessageNumber);
The network I/O part of downloading a message cannot really be optimized, but OpenPop.NET's parser is slow (based on my own performance tests).
MimeKit is 25x faster than OpenPop.NET at parsing email messages.
One of the main performance problems in OpenPop.NET's MIME parser is the fact that it uses a StreamReader for parsing (which is slow due to unnecessary charset conversion, reading 1 line at a time, etc - I have an analysis of another email library that uses StreamReader for parsing here: https://stackoverflow.com/a/18787176/87117).
Then there's the problem that OpenPop.NET's parser also uses Regex to remove CFWS (Comments and Folding White Space) from a header string before parsing/decoding it. This is expensive. It's far better to write a good tokenizer that can deal with CFWS.
If you are interested in some of the other techniques I used to optimize MimeKit to be so fast (as fast or faster than highly optimized C implementations), I wrote some blog posts about this:
Optimization Tricks used by MimeKit: Part 1
The summary of the optimization I talk about in part 1 is replacing loops like this that scan for the end of a line:
while (*inptr != (byte) '\n')
inptr++;
with a faster loop, like this:
int* dword = (int*) inptr;
do {
mask = *dword++ ^ 0x0A0A0A0A;
mask = ((mask - 0x01010101) & (~mask & 0x80808080));
} while (mask == 0);
inptr = (byte*) (dword - 1);
while (*inptr != (byte) '\n')
inptr++;
which improved performance by 20% (although on non-x86 architectures, it requires 'dword' to be 4-byte aligned).
Optimization Tricks used by MimeKit: Part 2
In part 2, I talk about writing a more optimized version of System.IO.MemoryStream. The problem with MemoryStream is that it has to keep 1 contiguous block of memory with the content, which means that as you write more data to it and it has to resize its internal byte array, it has to copy the content to the new array (which is expensive, especially once the amount of data in the stream is large).
To work around this performance bottleneck, I wrote a MemoryBlockStream which does not need to use a contiguous block of memory - it uses a linked list of byte arrays. Instead of having to resize the byte array when you overflow the current buffer, it simply allocates another 2048-byte array that the data will overflow into and appends it to the linked list.
Note: MimeKit itself only does email parsing, it doesn't do POP3 or SMTP or IMAP. If you want that kind of functionality, I've also written a library built on MimeKit that does that as well: MailKit
Update:
Sample code using MailKit (as requested) to download/parse all messages:
using System;
using System.Net;
using MailKit.Net.Pop3;
using MailKit;
using MimeKit;
namespace TestClient {
class Program
{
public static void Main (string[] args)
{
using (var client = new Pop3Client ()) {
client.Connect ("pop.gmail.com", 995, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove ("XOAUTH2");
client.Authenticate ("joey#gmail.com", "password");
int count = client.GetMessageCount ();
for (int i = 0; i < count; i++) {
var message = client.GetMessage (i);
Console.WriteLine ("Subject: {0}", message.Subject);
}
client.Disconnect (true);
}
}
}
}