Asynchronous tcp client in C# connecting to API... Sporadic errors - c#

I am writing a TCP Client in C# that is meant to connect to a 3rd-party API server. The vendor is insisting the problem is our code. They have provided a sample TCP client .exe to test against their API. I am generally successful connecting to their API with their app and using Packet Sender. I cannot figure out the problem in my code, though, so I'm hoping I can get some help seeing where I'm going wrong.
I have to make an initial call to ensure a setting is correct, then I can make my API call. It seems as though the API call only returns results when I make my call asynchronously. I'll get successful results 2 or 3 times in a row, and then I'll get an error message that is indecipherable without access to the vendor's source code. I got this code from an MSDN sample or someplace else reputable, but I still seem to get an error from the API (it hangs up and stops responding until I restart it) every 5th call or so (it's inconsistent when I get the error). Can someone help me debug my code? I'm genericizing it so that I'm not putting anything specific to the vendor's API here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace tcp
{
class AsyncClient
{
static void Main(string[] args)
{
SetInitialSetting();
ConnectAsTcpClient();
Console.ReadLine();
}
private static void SetInitialSetting()
{
byte[] data = new byte[1024];
string stringData;
IPAddress ip;
int port;
int recv;
IPAddress.TryParse(#"127.0.0.1", out ip);
port = 10000;
SetInitialSettingString();
Console.WriteLine("[Client] Writing request {0}", ClientRequestString);
using (var client = new TcpClient(ip.ToString(), port))
{
NetworkStream ns = client.GetStream();
ns.Write(ClientRequestBytes, 0, ClientRequestBytes.Length);
stringData = "";
do
{
recv = ns.Read(data, 0, data.Length);
stringData = stringData + Encoding.UTF8.GetString(data, 0, recv);
}
while (ns.DataAvailable);
Console.WriteLine("[Client] Server response was {0}", stringData);
ns.Close();
}
}
private static async void ConnectAsTcpClient()
{
byte[] data = new byte[10025];
IPAddress ip;
int port;
IPAddress.TryParse(#"127.0.0.1", out ip);
port = 10000;
using (var tcpClient = new TcpClient())
{
Console.WriteLine("[Client] Connecting to server");
await tcpClient.ConnectAsync(ip, port);
Console.WriteLine("[Client] Connected to server");
using (var networkStream = tcpClient.GetStream())
{
SetAPICallString();
Console.WriteLine("[Client] Writing request {0}", ClientRequestString);
await networkStream.WriteAsync(ClientRequestBytes, 0, ClientRequestBytes.Length);
var buffer = new byte[4096];
var byteCount = await networkStream.ReadAsync(buffer, 0, buffer.Length);
var response = Encoding.UTF8.GetString(buffer, 0, byteCount);
Console.WriteLine("[Client] Server response was {0}", response);
}
}
}
private static string ClientRequestString;
private static byte[] ClientRequestBytes;
private static void SetInitialSettingString()
{
ClientRequestString = "StringThatSetsTheAPISettingGoesHere";
ClientRequestBytes = Encoding.UTF8.GetBytes(ClientRequestString);
}
private static void SetAPICallString()
{
ClientRequestString = "StringForTheCallToTheAPIThatINeedGoesHere";
ClientRequestBytes = Encoding.UTF8.GetBytes(ClientRequestString);
}
}
}

Related

C# create multi threaded socket server and select client/connection

Currently, I'm programming in C#, I would like to modify the code below to isolate one client connection. so like creating a break-out room from the main pool.
Below are 2 files, one is just the basic standard .NET Framework Console App Program.cs file; the other is the server file. Combined, they both can make a multi-threaded server, but I would like one that allows me to also select a client to connect to in case if I were to create a remote control application as my friend did.
On a side note, I would like to share that I want to be able to connect to a client by entering connect [1,2,3,etc..] into the console.
Answers
If you answer, please put some code, It would really, really help. I learn a lot better by looking att the code rather than reading documentation.
Code
Server.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace TCPServer
{
class Server
{
TcpListener server = null;
int counter = 0;
public Server(string ip, int port)
{
IPAddress localAddr = IPAddress.Parse(ip);
server = new TcpListener(localAddr, port);
server.Start();
StartListener();
}
public void StartListener()
{
try
{
while (true)
{
Console.WriteLine("Waiting for incoming connections...");
TcpClient client = server.AcceptTcpClient();
counter += 1;
Console.WriteLine("Connected to authorized client: {0}", counter);
Thread t = new Thread(new ParameterizedThreadStart(HandleDeivce));
t.Start(client);
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
server.Stop();
}
}
public void HandleDeivce(Object obj)
{
TcpClient client = (TcpClient)obj;
var stream = client.GetStream();
string imei = String.Empty;
string data = null;
Byte[] bytes = new Byte[256];
int i;
try
{
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
string hex = BitConverter.ToString(bytes);
data = Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("{1}: Received: {0}", data, Thread.CurrentThread.ManagedThreadId);
if (data != "auth token")
{
stream.Close();
client.Close();
}
string str = "Device authorization successfull";
Byte[] reply = System.Text.Encoding.ASCII.GetBytes(str);
stream.Write(reply, 0, reply.Length);
Console.WriteLine("{1}: Sent: {0}", str, Thread.CurrentThread.ManagedThreadId);
}
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e.ToString());
client.Close();
}
}
}
}
Program.cs
using System;
using System.Threading;
namespace TCPServer
{
class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(delegate()
{
Server server = new Server("127.0.0.1", 13000);
});
thread.Start();
Console.WriteLine("Server started...");
}
}
}

C# UDP Packet Send & Receive

I'm trying to figure out how I can send & receive data via the UDP protocol using C# as a client, and having a JS server running that will transmit a "response" packet for testing purposes.
Below is the UDP class that I made for testing, it's simply just 2 threads:
One that read the received data from UDP and another thread for sending data.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace UDPTesting {
class UDPHandler {
private int receivePort, sendPort;
private string serverIP;
private IPEndPoint sendEndPoint, receiveEndPoint;
public UDPHandler(string serverIP, int receivePort, int sendPort) {
this.serverIP = serverIP;
this.receivePort = receivePort;
this.sendPort = sendPort;
this.sendEndPoint = new IPEndPoint(IPAddress.Parse(this.serverIP), this.sendPort);
this.receiveEndPoint = new IPEndPoint(IPAddress.Parse(this.serverIP), this.receivePort);
this.readerUdpClient();
this.senderUdpClient();
}
void readerUdpClient() {
UdpClient readerClient = new UdpClient();
IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Any, 3000);
readerClient.Client.Bind(localEndpoint); //Tried both Connect and Bind
//readerClient.Connect(this.receiveEndPoint);
Thread t = new Thread(() => {
Console.WriteLine("Awaiting data from server...");
var remoteEP = new IPEndPoint(IPAddress.Any, 3000);
byte[] bytesReceived = readerClient.Receive(ref remoteEP);
//The above throws: System.InvalidOperationException: 'You must call the Bind method before performing this operation'
Console.WriteLine("Received data from " + remoteEP.ToString());
});
t.Start();
}
void senderUdpClient() {
UdpClient senderClient = new UdpClient();
senderClient.Connect(this.sendEndPoint);
string sendString = "1;2;3";
byte[] bytes = toBytes(sendString);
Thread t = new Thread(() => {
while (true) {
senderClient.Send(bytes, bytes.Length);
Thread.Sleep(1000);
}
});
t.Start();
}
public byte[] toBytes(string text) {
return Encoding.UTF8.GetBytes(text);
}
public string fromBytes(byte[] bytes) {
return Encoding.UTF8.GetString(bytes);
}
}
}
Additionally my "main" for my program is this:
using System;
using System.Threading;
namespace UDPTesting {
class Program {
static void Main(string[] args) {
string serverIP = "46.101.102.243";
int sendPort = 41234;
int receivePort = 3000;
UDPHandler handler = new UDPHandler(serverIP, receivePort, sendPort);
}
}
}
How can I read the response that the server sends in the client, at the same time as I send data from the client to the server?
We tested it individually:
1) Sending a UDP packet from client -> server works, as I can see that the server receives the packet.
2) Sending a UDP packet from server -> client works, as I can see the client receives the packet.
3) When the client tries to send and read simultaneously, it will send data to the server, but will not read the response.
Any help would be greatly appreciated!
Your receiving function should work like this
void readerUdpClient()
{
new Thread(() => {
UdpClient readerClient = new UdpClient(receivePort);
Console.WriteLine("Awaiting data from server...");
var remoteEP = new IPEndPoint(IPAddress.Any, 0);
byte[] bytesReceived = readerClient.Receive(ref remoteEP);
Console.WriteLine($"Received {bytesReceived.Length} bytes from {remoteEP}");
}).Start();
}
The UdpClient constructor that takes a port automatically binds the local end point for you.

How do I send a list of connected clients from the server to the client, differentiating between a regular message?

I am rather new to network programming. I have done tons of googling and research over the past few days and have a chat application that can have multiple users connected to the server and are able to send messages to each other.
Right now there are no catches or methods for a client disconnecting, which I will add at a later date. However right now, I wish to add the functionality of showing a list of online users in a textbox on my client-side form.
When a client connects to the server, the server adds this client to the 'clientList'. However, I am a bit confused on how I would go about sending this list over to the client, but more importantly, how I would make the client recognise that this is not a regular message, more-so a list of clients.
I thought about making it so it sends it with a unique string of characters and doing an if statement, but I know there is a better way of doing it.
On the client-side code, I have a background worker that listens for data from the server. Surely if I serialise the list into a binary formatter, it will be picked up by my 'message listener', and the program will get confused on what is a message and what is data for the connected clients. Therefore I am not sure how I would differentiate between the two.
By no means am I asking you to code for me. I am simply looking for advice from those who have more wisdom and experience in the field. If I could get some pointers on the best way to approach this, I would be more than grateful. I appreciate your time.
Cient side code --
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace socketClientForm
{
public partial class Form1 : Form
{
private static byte[] buffer = new byte[1024];
private static Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public string message = "";
public Form1()
{
InitializeComponent();
this.Text = "Client";
}
delegate void SetTextCallback();
private void SetText()
{
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { });
}
else
this.chatBox.AppendText(message);
}
private void LoopConnect()
{
int attempts = 0;
while (!clientSocket.Connected)
try
{
attempts++;
clientSocket.Connect(IPAddress.Parse(IPBox.Text), 8080);
}
catch (SocketException)
{
chatBox.Clear();
chatBox.AppendText("Connection attempts: " + attempts.ToString());
}
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(backgroundWorker1.RunWorkerAsync), clientSocket);
chatBox.Clear();
chatBox.AppendText("Connected \n");
}
private void submitButton_Click(object sender, EventArgs e)
{
if (!String.IsNullOrWhiteSpace(msgBox.Text))
{
string req = usernameBox.Text + ": " + msgBox.Text;
byte[] buffer = Encoding.ASCII.GetBytes(req);
clientSocket.Send(buffer);
msgBox.Text = "";
}
}
private void connectButton_Click(object sender, EventArgs e)
{
LoopConnect();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (clientSocket.Connected)
{
try
{
byte[] receivedBuf = new byte[1024];
int rec = clientSocket.Receive(receivedBuf);
byte[] data = new byte[rec];
Array.Copy(receivedBuf, data, rec);
message = Encoding.ASCII.GetString(data) + "\n";
SetText();
}
catch
{
}
}
}
}
}
Server side code --
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace socketServer
{
class Program
{
private static byte[] buffer = new byte[1024];
private static List<Socket> clientSockets = new List<Socket>();
private static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static void Main(string[] args)
{
Console.Title = "Server";
SetupServer();
Console.ReadLine();
}
private static void SetupServer()
{
Console.WriteLine("Setting up server...");
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
serverSocket.Listen(1);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), null);
}
private static void AcceptCallBack(IAsyncResult AR)
{
Socket socket = serverSocket.EndAccept(AR);
clientSockets.Add(socket);
Console.WriteLine("Client Connected");
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), null);
}
private static void ReceiveCallBack(IAsyncResult AR)
{
Socket socket = (Socket)AR.AsyncState;
int received = socket.EndReceive(AR);
byte[] dataBuff = new byte[received];
Array.Copy(buffer, dataBuff, received);
string text = Encoding.ASCII.GetString(dataBuff);
Console.WriteLine("Text received: " + text);
byte[] data = Encoding.ASCII.GetBytes(text);
foreach (Socket client in clientSockets)
{
client.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), client);
}
//socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
//socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
}
private static void SendCallback(IAsyncResult AR)
{
Socket socket = (Socket)AR.AsyncState;
socket.EndSend(AR);
}
}
}
It is harder to derive a messaging protocol if you are working with plain bytes and string messages.
You are best creating a model - something like
public class NetMessage{
public int MessageType{get;set;}
public dynamic Payload{get;set;}
}
So lets imaging MessageType 1 is your authentication request.
it would be something like
{ "MessageType":"1", "PayLoad":{
"Username":"Admin",
"Password":"Password123"
}
}
You can either serialize this to string and send it (via Newtonsoft.Json)
Or, as I prefer, using a binary formatter to convert the object to bytes directly and then sending the bytes over the network.
Sending the serialized to byte form data, will be just slightly more efficient than sending string info across the network.
Using the protocol described above, you can make your server perform a switch statement on the MessageType and then handle the logic differently.
In your question, you want to send a list of connected clients?
Use something like MessageType 99, and set the Payload to be a List of clients.
Just remember, you cannot serialize the TcpClient object and send it to a remote user and expect the object to function like a connected TcpClient.
You can at most send the remote ip and the port that the server is connected to.
So I would recommend sending a model that represents this data.
UPDATE:
At the moment, your background worker is receiving the data and processing it as byte -> text and then performing straight up business logic on the text.
What you should be using, is managed types, other than the string type.
String is too low level, you need some intermediary types to help manage the logic.
Use nuget package manager in visual studio to install Newtonsoft.Json
(or JSON.Net it is sometimes known as)
With Newtonsoft you can do the following.
Given a class that looks like this
public class MessageClass
{
public int MessageType{get;set;}
public dynamic Payload{get;set;}
}
You can do the following
string content = "{\"MessageType\":\"2\",\"Payload\":\"blah\"}";
This is a JSON formatted string, that represents a class instance.
In C# Code this object would be like this:
var message = new MessageClass();
message.MessageType=2;
message.Payload = "blah";
What Newtonsoft gives you, is the ability to turn strings into managed C# types.
Eg:
Remember our string above called 'content' ?
var managedObject = JsonConvert.DeserializeObject<MessageClass>(content);
Console.WriteLine(managedObject.MessageType); // will; write 2
What I propose, is that your client and server communicate via JSON Formatted objects which then gives you the ability to perform more advanced conditional statements and more accurate assertions.
Newtonsoft provides 2 key methods for you to use.
Documented on the newtonsoft website.
newtonsoft.com/json/help/html/Methods_T_Newtonsoft_Json_JsonConvert.htm
To turn a C# object into a string
JsonConvert.SerializeObject(object o);
JsonConvert.DeserializeObject(String value);
For the deserialize method, put the type you are deserializing it to, in where the T is.
Eg:
MessageClass msg = JsonConvert.DeserializeObject<MessageClass>(content);

Tcp .Send Doesn't work after using for the first time

I am UsingClient.Send(Encoding.ASCII.GetBytes(Message)); to send a message to the client, when i tried sending a second message it doesn't give any error so i think it send with no problem but it never reaches the client (127.0.0.1)
Code That Send
public void SendMessage(Socket _Client, string Message)
{
foreach (Socket Client in Clients)
{
IPEndPoint TargetEndPoint = _Client.LocalEndPoint as IPEndPoint;
IPAddress TargetIp = TargetEndPoint.Address;
int TargetPort = TargetEndPoint.Port;
IPEndPoint ClientEndPoint = Client.LocalEndPoint as IPEndPoint;
IPAddress ClientIp = ClientEndPoint.Address;
int ClientPort = ClientEndPoint.Port;
if (TargetIp.ToString() == ClientIp.ToString() && TargetPort == ClientPort)
{
Client.Send(Encoding.ASCII.GetBytes(Message));
//Client.EndSend();
}
}
}
Code That Receive
private void RecivedCallBack(IAsyncResult Result)
{
//Create a int with the Buffer Size
int BufferSize = _Socket.EndReceive(Result);
//Create a new byte array with the Buffer Size
byte[] Packet = new byte[BufferSize];
//Copy Buffer to Packet
Array.Copy(_Buffer, Packet, Packet.Length);
//Handle Packet
PacketHandler.Packet(Encoding.UTF8.GetString(Packet));
//Makes _Buffer a new byte
_Buffer = new byte[1024];
//Get Ready to recive data
_Socket.BeginReceive(_Buffer, 0, _Buffer.Length, SocketFlags.None, RecivedCallBack, null);
}
Code that Handle
public static void Packet(string Message)
{
Console.WriteLine(Message);
switch (Message)
{
case "StartChat":
_ChatForm Start = new _ChatForm();
Start.ShowDialog();
break;
case "StopChat":
_ChatForm._Chat.EndChat();
break;
}
}
TCP is stream based, so your client has no way to know when the message has ended. Either use UDP, implement a way to detect the end of messages (eg send a 4 byte message with the length of the real message, before sending the real message... and read on the client until the whole message has been received), or use a library. I like Hazel: https://github.com/DarkRiftNetworking/Hazel-Networking.
The great thing about Hazel is that it implements reliable UDP. So, if you need to have your "messages" arrive in the order in which they were sent, or if you need guaranteed delivery and receipt of such messages (such as what TCP provides), then you can do so with their reliable UDP implementation.
They will also implement Web Sockets at some point :) Good luck!
A client/server example from the documentation:
Server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using Hazel;
using Hazel.Tcp;
namespace HazelExample
{
class ServerExample
{
static ConnectionListener listener;
public static void Main(string[] args)
{
listener = new TcpConnectionListener(IPAddress.Any, 4296);
listener.NewConnection += NewConnectionHandler;
Console.WriteLine("Starting server!");
listener.Start();
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
listener.Close();
}
static void NewConnectionHandler(object sender, NewConnectionEventArgs args)
{
Console.WriteLine("New connection from " + args.Connection.EndPoint.ToString();
args.Connection.DataReceived += DataReceivedHandler;
args.Recycle();
}
private static void DataReceivedHandler(object sender, DataEventArgs args)
{
Connection connection = (Connection)sender;
Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
connection.SendBytes(args.Bytes, args.SendOption);
args.Recycle();
}
}
}
Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Hazel;
using Hazel.Tcp;
namespace HazelExample
{
class ClientExample
{
static Connection connection;
public static void Main(string[] args)
{
NetworkEndPoint endPoint = new NetworkEndPoint("127.0.0.1", 4296);
connection = new TcpConnection(endPoint);
connection.DataReceived += DataReceived;
Console.WriteLine("Connecting!");
connection.Connect();
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
connection.Close();
}
}
}

C# Receive Data from Multiple TCP clients on same server and display it in GUI

I am trying to develop a C# based GUI program to receive the data from multiple TCP clients on a single server. All the clients should listen to the same port on the server and the server accepts the data from multiple clients, divide the receive data and display it in GUI.
I tried this console based program to receive the data from multiple clients on port 4000 on server. But i am not able to connect to the clients. For a single client using the specific ip address and port binding, its working fine. It just waiting for the multiple clients, and do not receive the data from the clients. Can you tell me the specific solution how to tackle this problem?
Here is my code:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class ThreadedTcpSrvr
{
private TcpListener client;
public ThreadedTcpSrvr()
{
client = new TcpListener(4000);
client.Start();
while (true)
{
while (!client.Pending())
{
Thread.Sleep(100);
}
ConnectionThread newconnection = new ConnectionThread();
newconnection.threadListener = this.client;
Thread newthread = new Thread(new
ThreadStart(newconnection.HandleConnection));
newthread.Start();}
}
public static void Main()
{
ThreadedTcpSrvr server = new ThreadedTcpSrvr();
}
}
class ConnectionThread
{
public TcpListener threadListener;
private static int connections = 0;
public void HandleConnection()
{
int recv;
byte[] data = new byte[1024];
TcpClient client = threadListener.AcceptTcpClient();
NetworkStream ns = client.GetStream();
connections++;
Console.WriteLine("New client accepted: {0} active connections",
connections);
string welcome = "Welcome to the Server";
data = Encoding.ASCII.GetBytes(welcome);
ns.Write(data, 0, data.Length);
while (true)
{
data = new byte[1024];
recv = ns.Read(data, 0, data.Length);
if (recv == 0)
break;
ns.Write(data, 0, recv);
}
ns.Close();
client.Close();
connections--;
Console.WriteLine("Client disconnected: {0} active connections",
connections); }
}
P.S. I tried to capture the data through wireshark and i am able to capture the data.

Categories