Trouble with sockets - c#

I need make client-server game with 2 players. I bind client sockets on localhost and different ports. For connection clients send serialized msg with enum header and theirs IPEndPoints as value. When server receives connection messages from different clients, they are having same ports. How to fix it?
Method which receives connections:
public static void GetConnections()
{
while (true)
{
Console.WriteLine("Waiting for connections...");
var len = ServerSocket.Receive(TempData);
var msg = (NetMessage)DataSerializer.Deserialize(TempData);
Console.WriteLine(msg.Data.ToString());
if (msg.PacketType == PacketType.CONNECT)
{
String[] IP = msg.Data.ToString().Split(':');
if (Player1IP == null)
{
Player1IP = new IPEndPoint(IPAddress.Parse(IP[0]), Int32.Parse(IP[1]));
Console.WriteLine("Approved connection of player 1 with remote endpoint: " + Player1IP);
}
else if (Player2IP == null)
{
Player2IP = new IPEndPoint(IPAddress.Parse(IP[0]), Int32.Parse(IP[1]));
Console.WriteLine("Approved connection of player 2 with remote endpoint: " + Player2IP);
}
if (Player1IP != null && Player2IP != null)
{
Console.WriteLine("Both player are connected");
Console.WriteLine(Player1IP + " " + Player2IP);
return;
}
}
}
Connection method(runs once):
public void GetConnection()
{
NetMessage msg = new NetMessage(PacketType.CONNECT, ClientSocket.LocalEndPoint.ToString());
var bytes = DataSerializer.Serialize(msg);
ClientSocket.SendTo(bytes, ServerIP);
BeganConnection = true;
}

Related

Discover all devices on LAN

In my Xamarin Forms application, I am trying to discover all devices on the local network that I am connected to. My approach is to first get the device IP address, and use to first 3 numbers to know what the gateway is (first number is always 192). And then, ping every address on that gateway. Here is my code:
public partial class MainPage : ContentPage
{
private List<Device> discoveredDevices = new List<Device>();
public MainPage()
{
InitializeComponent();
Ping_all();
}
private string GetCurrentIp()
{
IPAddress[] addresses = Dns.GetHostAddresses(Dns.GetHostName());
string ipAddress = string.Empty;
if (addresses != null && addresses[0] != null)
{
ipAddress = addresses[0].ToString();
}
else
{
ipAddress = null;
}
return ipAddress;
}
public void Ping_all()
{
string ip = GetCurrentIp();
if (ip != null)
{
//Extracting and pinging all other ip's.
string[] array = ip.Split('.');
string gateway = array[0] + "." + array[1] + "." + array[2];
for (int i = 2; i <= 255; i++)
{
string ping_var = $"{gateway}.{i}";
//time in milliseconds
Ping(ping_var, 4, 4000);
}
}
}
public void Ping(string host, int attempts, int timeout)
{
for (int i = 0; i < attempts; i++)
{
new Thread(delegate ()
{
try
{
System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping();
ping.PingCompleted += new PingCompletedEventHandler(PingCompleted);
ping.SendAsync(host, timeout, host);
// PingCompleted never gets called
}
catch(Exception e)
{
// Do nothing and let it try again until the attempts are exausted.
// Exceptions are thrown for normal ping failurs like address lookup
// failed. For this reason we are supressing errors.
}
}).Start();
}
}
private void PingCompleted(object sender, PingCompletedEventArgs e)
{
string ip = (string)e.UserState;
if (e.Reply != null && e.Reply.Status == IPStatus.Success)
{
string hostname = GetHostName(ip);
string macaddres = GetMacAddress(ip);
var device = new Device()
{
Hostname = hostname,
IpAddress = ip,
MacAddress = macaddres
};
discoveredDevices.Add(device);
}
}
public string GetHostName(string ipAddress)
{
try
{
IPHostEntry entry = Dns.GetHostEntry(ipAddress);
if (entry != null)
{
return entry.HostName;
}
}
catch (SocketException)
{
}
return null;
}
public string GetMacAddress(string ipAddress)
{
string macAddress = string.Empty;
System.Diagnostics.Process Process = new System.Diagnostics.Process();
Process.StartInfo.FileName = "arp";
Process.StartInfo.Arguments = "-a " + ipAddress;
Process.StartInfo.UseShellExecute = false;
Process.StartInfo.RedirectStandardOutput = true;
Process.StartInfo.CreateNoWindow = true;
Process.Start();
string strOutput = Process.StandardOutput.ReadToEnd();
string[] substrings = strOutput.Split('-');
if (substrings.Length >= 8)
{
macAddress = substrings[3].Substring(Math.Max(0, substrings[3].Length - 2))
+ "-" + substrings[4] + "-" + substrings[5] + "-" + substrings[6]
+ "-" + substrings[7] + "-"
+ substrings[8].Substring(0, 2);
return macAddress;
}
else
{
return "OWN Machine";
}
}
}
I get to the part where I try to ping:
System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping();
ping.PingCompleted += new PingCompletedEventHandler(PingCompleted);
ping.SendAsync(host, timeout, host);
But PingCompleted never gets called. No exception is thrown either. Any idea why? I'm running this on a physical Android device.
EDIT
PingCompleted started getting called for me now, not sure why it wasn't working before. But it now crashes in my GetMacAddress function on the line Process.Start(); because it can not find the resource.
I ended up using this really robust and easy to use library:
https://github.com/Yortw/RSSDP
It doesn't actually find all devices on the network, instead it uses SSDP (Simple Search Discovery Protocol) to quickly find all devices that are broadcasting a service with this protocol on the network. I filtered it to only scan devices running my app, which is what I actually needed. It takes only a second to discover my devices, which is much faster than pinging 255 addresses.
In the documentation you will see:
var deviceDefinition = new SsdpRootDevice()
{
CacheLifetime = TimeSpan.FromMinutes(30), //How long SSDP clients can cache this info.
Location = new Uri("http://mydevice/descriptiondocument.xml"), // Must point to the URL that serves your devices UPnP description document.
DeviceTypeNamespace = "my-namespace",
DeviceType = "MyCustomDevice",
FriendlyName = "Custom Device 1",
Manufacturer = "Me",
ModelName = "MyCustomDevice",
Uuid = GetPersistentUuid() // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
};
For the Location I set it as my device's IP. So that another device that discovers it can have the IP too. I don't think it's meant to be used this way, but it worked for me and I don't see why not.
I tested it on 2 physical Android devices.

Get the live data on TCP server from UWP (UPDATED)

Below is one method that basically sends the data to TCP Server.
UPDATE BEGINS HERE:
//////////////////////////////////
private string FormatValueByPresentation(IBuffer buffer, GattPresentationFormat format)
{
// BT_Code: For the purpose of this sample, this function converts only UInt32 and
// UTF-8 buffers to readable text. It can be extended to support other formats if your app needs them.
byte[] data;
CryptographicBuffer.CopyToByteArray(buffer, out data);
if (format != null)
{
if (format.FormatType == GattPresentationFormatTypes.UInt32 && data.Length >= 4)
{
return BitConverter.ToInt32(data, 0).ToString();
}
else if (format.FormatType == GattPresentationFormatTypes.Utf8)
{
try
{
return Encoding.UTF8.GetString(data);
}
catch (ArgumentException)
{
return "(error: Invalid UTF-8 string)";
}
}
else
{
// Add support for other format types as needed.
return "Unsupported format: " + CryptographicBuffer.EncodeToHexString(buffer);
}
}
else if (data != null)
{
// We don't know what format to use. Let's try some well-known profiles, or default back to UTF-8.
if (selectedCharacteristic.Uuid.Equals(GattCharacteristicUuids.HeartRateMeasurement))
{
try
{
///////LOOK HERE/////
**string b = ParseHeartRateValue(data).ToString();
TrySend(b);
//return "Heart Rate: " + ParseHeartRateValue(data).ToString();
return "Heart Rate: " + b;**
}
catch (ArgumentException)
{
return "Heart Rate: (unable to parse)";
}
}
else if (selectedCharacteristic.Uuid.Equals(GattCharacteristicUuids.BatteryLevel))
{
try
{
// battery level is encoded as a percentage value in the first byte according to
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.battery_level.xml
return "Battery Level: " + data[0].ToString() + "%";
}
catch (ArgumentException)
{
return "Battery Level: (unable to parse)";
}
}
// This is our custom calc service Result UUID. Format it like an Int
else if (selectedCharacteristic.Uuid.Equals(Constants.ResultCharacteristicUuid))
{
return BitConverter.ToInt32(data, 0).ToString();
}
// No guarantees on if a characteristic is registered for notifications.
else if (registeredCharacteristic != null)
{
// This is our custom calc service Result UUID. Format it like an Int
if (registeredCharacteristic.Uuid.Equals(Constants.ResultCharacteristicUuid))
{
return BitConverter.ToInt32(data, 0).ToString();
}
}
else
{
try
{
return "Unknown format: " + Encoding.UTF8.GetString(data);
}
catch (ArgumentException)
{
return "Unknown format";
}
}
}
else
{
return "Empty data received";
}
return "Unknown format";
}
///////// END OF UPDATE //////
private async void TrySend(string data)
{
// Create the StreamSocket and establish a connection to the echo server.
StreamSocket socket = new StreamSocket();
try
{
var streamSocket = new Windows.Networking.Sockets.StreamSocket();
{
//The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
var hostName = new Windows.Networking.HostName("127.0.0.1");
await streamSocket.ConnectAsync((new Windows.Networking.HostName("127.0.0.1")), "9999");
// Send a request to the echo server.
using (Stream outputStream = streamSocket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
while (true)
{
await streamWriter.WriteLineAsync(data);
await streamWriter.FlushAsync();
}
//await streamWriter.WriteLineAsync(data);
//await streamWriter.FlushAsync();
}
}
}
}
catch (Exception)
{
}
}
And here is my TCP Server code that receives the data:
public class EchoServer {
public static void Main() {
TcpListener listener = null;
try
{
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 9999);
listener.Start();
Console.WriteLine("TCP Server Has Started....");
while (true)
{
Console.WriteLine(" ");
Console.WriteLine("Waiting for incoming client connections....");
Console.WriteLine(" ");
Console.WriteLine("A message will display below once the client starts and establishes a connection ");
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine(" ");
Console.WriteLine("Okay, Accepting Client connection now");
Console.WriteLine(" ");
Console.WriteLine("Accepted new client connection.....");
StreamReader reader = new StreamReader(client.GetStream());
StreamWriter writer = new StreamWriter(client.GetStream());
string s = string.Empty;
while (!(s = reader.ReadLine()).Equals("Exit") || (s == null)) {
Console.WriteLine("From client -> " + s);
writer.WriteLine("From server -> " + s);
writer.Flush();
}
reader.Close();
writer.Close();
client.Close();
}
} catch (Exception e)
{
Console.WriteLine(e);
} finally
{
if (listener != null)
{
listener.Stop();
}
}
}
}
Now, the data I am trying to get are the heart rates and it changes every two seconds. However on TCP server I only get the first recorded value of a heart rate and it keeps repeating instead of getting new one.
There is a similar post I saw here on stackoverflow : UWP TCP receive data continuously
and someone suggested to use while loop which I did as you can see in the code.
Are there any other suggestions on what should I do?
Thanks
while (true)
{
await streamWriter.WriteLineAsync(data);
await streamWriter.FlushAsync();
}
The while(true) will keep repeating, meaning that it will always send 'data' at its current value. This is what causes your issue.
In my opinion, you should keep a connection to your TCP server open outside of your 'TrySend' method, and use this method only to send the data. This way you won't need to use this loop.
EDIT :
Try this :
private async void CharacteristicReadButton_Click()
{
while(true)
{
// BT_Code: Read the actual value from the device by using Uncached.
GattReadResult result = await selectedCharacteristic.ReadValueAsync(BluetoothCacheMode.Uncached);
if (result.Status == GattCommunicationStatus.Success)
{
string formattedResult = FormatValueByPresentation(result.Value, presentationFormat);
rootPage.NotifyUser($"Read result: {formattedResult}", NotifyType.StatusMessage);
//string formattedResult = FormatValueByPresentation(result.Value, presentationFormat);
//rootPage.NotifyUser($"Read result: {formattedResult}", NotifyType.StatusMessage);
}
else
{
rootPage.NotifyUser($"Read failed: {result.Status}", NotifyType.ErrorMessage);
}
}
}

Server client with dynamic ip adress

I have an a server application built in C# witch listens a tcp port an receive some data packages from clients. The problem is there are clients with dynamic ip address, so at a moment i have one client with more connections opened. I need a way to determine when the client changed his ip and close the old connection.
List<Socket> ClientsToRemove = new List<Socket>();
---this foreach this to avoid connections with the same ip address---
foreach(Socket client1 in AllConnections)
{
int count = 0;
foreach(Socket client2 in AllConnections)
{
if(((IPEndPoint)client1.RemoteEndPoint).Address.ToString() == ((IPEndPoint)client2.RemoteEndPoint).Address.ToString())
{
count++;
}
}
if(count>1)
{
AllConnections.Remove(client1);
UpdateActiveConnectionsBox(AllConnections.Count.ToString());
}
}
---verify active connections---
foreach (Socket client in AllConnections)
{
if (!client.IsConnected())
{
ClientsToRemove.Add(client);
}
else
{
UpdateLogBox("Active client IP:" + ((IPEndPoint)client.RemoteEndPoint).Address.ToString());
}
}
UpdateLogBox("ACtive connection numer: " + AllConnections.Count.ToString());
foreach (Socket client in ClientsToRemove)
if (AllConnections.Contains(client))
AllConnections.Remove(client);
ClientsToRemove.Clear();
ClientsToRemove = null;
UpdateActiveConnectionsBox(AllConnections.Count.ToString());
Thread.Sleep(1500);
And this is where i accept new connections:
ServerMainSocket.Listen(10);
Socket TempSock = ServerMainSocket.Accept();
Task.Factory.StartNew(() => { ClientFunction(TempSock); });
UpdateLogBox("New client connected from IP:" + ((IPEndPoint)TempSock.RemoteEndPoint).Address.ToString());
if(AllConnections.Contains(TempSock) != true)
{
AllConnections.Add(TempSock);
}
else
{
AllConnections.Remove(TempSock);
AllConnections.Add(TempSock);
UpdateActiveConnectionsBox(AllConnections.Count.ToString());
}

C# Socket: Client Mishandle 'a' as the Client's id

There are two programs that I made that didn't work. There are the server and the client. The server accepts many client by giving a user an ID (starting from 0). The server sends out the command to the specific client based up the server's id. (Example: 200 client are connected to 1 server. The server's selected id is '5', so the server will send the command to all of the client, and the client will ask the server what ID he wants to execute his command on, and if it's '5', that client will execute and send data to the server). The client has many commands, but to create the smallest code with the same error, I only use 1 command (dir). Basically, the server sends the command to the client and if it matches with the client current id and the server current id, it will process the command. By default, the server's current ID is 10. Here are the list of the commands to help the people who wants to answer:
Server Command:
list (Shows all of the users ID connected and the server's current ID) --> Happens on server
dir (request the client to send its dir listing) --> Sent by the client, read by the Server
set (set the server's current id to any number) (example: 'set 4')
Client:
using System;
using System.Speech.Synthesis;
using System.Windows.Forms;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
namespace clientControl
{
class Program
{
public static string directory = #"C:\";
public static int id = -10;
public static Socket sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static void Main(string[] args)
{
Connect();
getSession();
ReadResponse(sck);
}
static byte[] readResponseFunc()
{
long fileSize = 0;
string fileSizeInString = null;
byte[] fileSizeInByteArray = new byte[1024];
int fileSizeLength = sck.Receive(fileSizeInByteArray);
for (int i = 0; i < fileSizeLength; i++)
{
fileSizeInString = fileSizeInString + (char)fileSizeInByteArray[i];
}
try
{
fileSize = Convert.ToInt64(fileSizeInString);
}
catch { Console.WriteLine(fileSizeInString); }
sck.Send(Encoding.ASCII.GetBytes("a"));
byte[] responseUnknown = new byte[1];
sck.Receive(responseUnknown);
if (Encoding.ASCII.GetString(responseUnknown) == "b")
{
byte[] dataInByteArray = new byte[fileSize];
int dataLength = sck.Receive(dataInByteArray);
return dataInByteArray;
}
return Encoding.ASCII.GetBytes("ERROR RESPONSE FUNC");
}
static void getSession()
{
byte[] message_1 = Encoding.ASCII.GetBytes("make_id");
sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
byte[] responseUnknown = new byte[1];
if (Encoding.ASCII.GetString(responseUnknown) == "a")
{
sck.Send(Encoding.ASCII.GetBytes("b"));
sck.Send(message_1);
}
byte[] receivedID = readResponseFunc();
id = Convert.ToInt32(Encoding.ASCII.GetString(receivedID));
}
static bool SocketConnected(Socket s)
{
bool part1 = s.Poll(1000, SelectMode.SelectRead);
bool part2 = (s.Available == 0);
if (part1 && part2)
return false;
else
return true;
}
static void ReadResponse(Socket sck)
{
while (true)
{
if (SocketConnected(sck) == true)
{
try
{
string response = Encoding.ASCII.GetString(readResponseFunc());
byte[] message_1 = Encoding.ASCII.GetBytes("get_id");
sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
byte[] responseUnknown = new byte[1];
if (Encoding.ASCII.GetString(responseUnknown) == "a")
{
sck.Send(Encoding.ASCII.GetBytes("b"));
sck.Send(message_1);
}
byte[] response_2InByteArray = readResponseFunc();
string response_2 = Encoding.ASCII.GetString(response_2InByteArray);
if (Convert.ToInt32(response_2) == id)
{
if (response == "dir")
{
string resultOfDirring = "Current Directory: " + directory + "\n\n";
string[] folderListingArray = Directory.GetDirectories(directory);
foreach (string dir in folderListingArray)
{
string formed = "DIRECTORY: " + Path.GetFileName(dir);
resultOfDirring = resultOfDirring + formed + Environment.NewLine;
}
string[] fileListingArray = Directory.GetFiles(directory);
foreach (var file in fileListingArray)
{
FileInfo fileInfo = new FileInfo(file);
string formed = "FILE: " + Path.GetFileName(file) + " - FILE SIZE: " + fileInfo.Length + " BYTES";
resultOfDirring = resultOfDirring + formed + Environment.NewLine;
}
byte[] message_11 = Encoding.ASCII.GetBytes(resultOfDirring);
sck.Send(Encoding.ASCII.GetBytes(message_11.Length.ToString()));
byte[] responseUnknown_11 = new byte[1];
if (Encoding.ASCII.GetString(responseUnknown_11) == "a")
{
sck.Send(Encoding.ASCII.GetBytes("b"));
sck.Send(message_11);
}
}
}
else { }
}
catch { if (SocketConnected(sck) == false) { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }; }
}
else if (SocketConnected(sck) == false) { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }
}
}
static void Connect()
{
while (true)
{
try
{
sck.Connect(IPAddress.Parse("127.0.0.1"), 80);
break;
}
catch { }
}
}
}
}
Server:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Threading;
namespace serverControl
{
class Program
{
public static int ftpNum = 1;
public static List<int> listOfClient = new List<int>();
public static TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
public static string message = null;
public static int id = 0;
public static int selected_id = 10;
static void Main(string[] args)
{
server.Start();
Thread startHandlingClientThread = new Thread(startHandlingClient);
startHandlingClientThread.Start();
while (true)
{
Console.Write(":> ");
string rawmessage = Console.ReadLine();
if (rawmessage == "list")
{
Console.WriteLine("SELECTED ID: " + selected_id);
Console.WriteLine("List of Clients ID:");
for (int i = 0; i < listOfClient.Count; i++)
{
Console.WriteLine(listOfClient[i]);
}
message = rawmessage+"PREVENT_REPETITION_IN_COMMAND";
}
else if (rawmessage.Contains("set ")) { int wantedChangeId = Convert.ToInt32(rawmessage.Replace("set ", "")); selected_id = wantedChangeId; message = rawmessage+ "PREVENT_REPETITION_IN_COMMAND"; }
else
{
message = rawmessage;
}
}
}
static byte[] readResponseFunc(Socket sck)
{
long fileSize = 0;
string fileSizeInString = null;
byte[] fileSizeInByteArray = new byte[1024];
int fileSizeLength = sck.Receive(fileSizeInByteArray);
for (int i = 0; i < fileSizeLength; i++)
{
fileSizeInString = fileSizeInString + (char)fileSizeInByteArray[i];
}
fileSize = Convert.ToInt64(fileSizeInString);
sck.Send(Encoding.ASCII.GetBytes("a"));
byte[] responseUnknown = new byte[1];
sck.Receive(responseUnknown);
if (Encoding.ASCII.GetString(responseUnknown) == "b")
{
byte[] dataInByteArray = new byte[fileSize];
int dataLength = sck.Receive(dataInByteArray);
return dataInByteArray;
}
return Encoding.ASCII.GetBytes("ERROR RESPONSE FUNC");
}
static void startHandlingClient()
{
while (true)
{
handleClient(server);
}
}
static void handleClient(TcpListener clientToAccept)
{
Socket sck = clientToAccept.AcceptSocket();
Thread myNewThread = new Thread(() => ReadResponse(sck));
myNewThread.Start();
}
static bool SocketConnected(Socket s)
{
bool part1 = s.Poll(1000, SelectMode.SelectRead);
bool part2 = (s.Available == 0);
if (part1 && part2)
return false;
else
return true;
}
static void ReadResponse(Socket sck)
{
Thread myNewThread = new Thread(() => SendtoClient(sck));
myNewThread.Start();
Thread.Sleep(2000);
while (true)
{
if (SocketConnected(sck) == true)
{
try
{
byte[] dataInByteArray = readResponseFunc(sck);
string response = Encoding.ASCII.GetString(dataInByteArray);
Console.WriteLine("res: " + response);
if (response != "make_id" && response != "get_id") { Console.WriteLine(response); }
if (response == "make_id")
{
Console.WriteLine("Someone wants an ID");
byte[] message_1 = Encoding.ASCII.GetBytes(id.ToString());
listOfClient.Add(id);
// START
sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
byte[] responseUnknown = new byte[1];
if (Encoding.ASCII.GetString(responseUnknown) == "a")
{
sck.Send(Encoding.ASCII.GetBytes("b"));
sck.Send(message_1);
}
id++;
}
if (response == "get_id")
{
byte[] message_1 = Encoding.ASCII.GetBytes(selected_id.ToString());
sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
byte[] responseUnknown = new byte[1];
if (Encoding.ASCII.GetString(responseUnknown) == "a")
{
sck.Send(Encoding.ASCII.GetBytes("b"));
sck.Send(message_1);
}
}
}
catch { if (SocketConnected(sck) == false) { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }; }
}
else if (SocketConnected(sck) == false) { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }
}
}
static void SendtoClient(Socket sck)
{
string tempmessage = null;
while (true)
{
if (SocketConnected(sck) == true)
{
if (tempmessage != message)
{
if (!message.Contains("PREVENT_REPETITION_IN_COMMAND"))
{
byte[] message_1 = Encoding.ASCII.GetBytes(message);
sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
byte[] responseUnknown = new byte[1];
if (Encoding.ASCII.GetString(responseUnknown) == "a")
{
sck.Send(Encoding.ASCII.GetBytes("b"));
sck.Send(message_1);
}
}
tempmessage = message;
}
}
else if (SocketConnected(sck) == false)
{ Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }
}
}
}
}
Problem:
The problem is within the GetSession or the ReadResponseFunc function. The client thinks that his ID received by the server is 'a' (it's suppose to be an integer). I don't want a solution that suggest me to use other libs or
the TcpClient class
Bounty:
I'll put up a bounty with no expiry time to those who solve the problem.
The logic in your code is very confusing. My question to you: Why are you sending 'a' and 'b' back and forth between the server and client? Is it some sort of confirmation that the message has been received?
Anyways, throughout the quick tests I've done just now, it seems that the problem is Line 59 of your server:
sck.Send(Encoding.ASCII.GetBytes("a"));
Here's what I figured out during testing:
Server executes.
Client executes.
Client sends server the length of "make_id" (Line 51 of client)
Client waits for a response to return.
Server reads the value and sends back 'a' (Line 59 of server)
You may want to spend some time to straighten out your protocol so it's less confusing and more organized. That would help people like me and you spot bugs much more easily.
The user 'Bobby' has already found your problem. Credits go to him.
I further suggest that you use less threads, as thread synchronisation needs some effort when doing it right: all data that is accessed from different threads must be secured by locks, so that no outdated values remain in the CPU caches. Use .net Monitor from the "threading synchronisation primitives" for that job.
About the threads themselves:
You should have only one thread in the server. This thread takes all clients from a list (secured by Monitor), in which they were added when connection attempts were received. On each client it checks for incoming messages, evaluates them and replies with own messages if needed.
The client also has just one thread, that will loop (dont forget a sleep in the loop or you will have 100% usage of the used CPU core), send messages when desired and wait for replies when messages were sent.
About the messages:
I already gave some proposals in a comment to Bobby's answer. I suggest you create a CMessage class that has a Serialize() and Deserialze() to create a byte array to send from the CMessage members (serializing the content) or vice versa filling the members from the received bytes. You then may use this class in both programs and you'll have common solution.

C# as Websocket server for HTML5 websocket connection

I guess it's already time that I ask others. Is it possible to create a websocket server using C# and server request from HTML5 codes?
I am currently using the System package for websocket. I have a code that I downloaded over the internet and here it is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WebSocketChatServer
{
using WebSocketServer;
class ChatServer
{
WebSocketServer wss;
List<User> Users = new List<User>();
string unknownName = "john doe";
public ChatServer()
{
// wss = new WebSocketServer(8181, "http://localhost:8080", "ws://localhost:8181/chat");
wss = new WebSocketServer(8080, "http://localhost:8080", "ws://localhost:8080/dotnet/Chats");
wss.Logger = Console.Out;
wss.LogLevel = ServerLogLevel.Subtle;
wss.ClientConnected += new ClientConnectedEventHandler(OnClientConnected);
wss.Start();
KeepAlive();
}
private void KeepAlive()
{
string r = Console.ReadLine();
while (r != "quit")
{
if(r == "users")
{
Console.WriteLine(Users.Count);
}
r = Console.ReadLine();
}
}
void OnClientConnected(WebSocketConnection sender, EventArgs e)
{
Console.WriteLine("test");
Users.Add(new User() { Connection = sender });
sender.Disconnected += new WebSocketDisconnectedEventHandler(OnClientDisconnected);
sender.DataReceived += new DataReceivedEventHandler(OnClientMessage);
}
void OnClientMessage(WebSocketConnection sender, DataReceivedEventArgs e)
{
Console.WriteLine(sender);
User user = Users.Single(a => a.Connection == sender);
if (e.Data.Contains("/nick"))
{
string[] tmpArray = e.Data.Split(new char[] { ' ' });
if (tmpArray.Length > 1)
{
string myNewName = tmpArray[1];
while (Users.Where(a => a.Name == myNewName).Count() != 0)
{
myNewName += "_";
}
if (user.Name != null)
wss.SendToAll("server: '" + user.Name + "' changed name to '" + myNewName + "'");
else
sender.Send("you are now know as '" + myNewName + "'");
user.Name = myNewName;
}
}
else
{
string name = (user.Name == null) ? unknownName : user.Name;
wss.SendToAllExceptOne(name + ": " + e.Data, sender);
sender.Send("me: " + e.Data);
}
}
void OnClientDisconnected(WebSocketConnection sender, EventArgs e)
{
try
{
User user = Users.Single(a => a.Connection == sender);
string name = (user.Name == null) ? unknownName : user.Name;
wss.SendToAll("server: "+name + " disconnected");
Users.Remove(user);
}
catch (Exception exc)
{
Console.WriteLine("ehm...");
}
}
}
}
And I have this code for client side:
<!HTML>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
</script>
<script language="javascript" type="text/javascript">
jQuery(document).ready(function(){
var socket = new WebSocket("ws://localhost:8080");
socket.onopen = function(){
alert("Socket has been opened!");
}
});
</script>
</head>
</HTML>
As I run my C# console app and load the client page, the app tells me that there's someone who connected in the port it is listening to. But on my client side, as I look in firebug's console, it gives me the beginner's classic error:
Firefox can't establish a connection to the server at ws://localhost:8080/
What I would like to achieve is establish first a successful websocket connection and push value to my client coming from my server.
I have considered Alchemy but the version I have is Visual Studio express 2010, the free version, and it says that "solution folders are not supported in this version of application".
Any help will be very much appreciated.
I've been developing a server for a JavaScript/HTML 5 game for about 7 months now have you looked into Alchemy Websockets? its pretty easy to use.
Example:
using Alchemy;
using Alchemy.Classes;
namespace GameServer
{
static class Program
{
public static readonly ConcurrentDictionary<ClientPeer, bool> OnlineUsers = new ConcurrentDictionary<ClientPeer, bool>();
static void Main(string[] args)
{
var aServer = new WebSocketServer(4530, IPAddress.Any)
{
OnReceive = context => OnReceive(context),
OnConnected = context => OnConnect(context),
OnDisconnect = context => OnDisconnect(context),
TimeOut = new TimeSpan(0, 10, 0),
FlashAccessPolicyEnabled = true
};
}
private static void OnConnect(UserContext context)
{
var client = new ClientPeer(context);
OnlineUsers.TryAdd(client, false);
//Do something with the new client
}
}
}
As you can see its pretty easy to work with and I find their documentation very good (note ClientPeer is a custom class of mine just using it as an example).
What you are trying to achieve will be far easier if you take a look at ASP.NET SignalR
It has support for high level hubs to implement realtime communication and also has a persistent connection low level class to have a finely grained control over the communication.
Support for multiple client types and fallback if websockets isn't supported at both ends of the communication (it can optionally use long polling or forever frames).
The reason for this error is ( probably ) because you are not responding to the handshake. Once the connection is established browser sends some data and the server must respond appropriately ( otherwise browser will close the connection ). You can read more about this on wiki or directly in specification.
I modified a code i downloaded online and here's what I got now:
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
namespace WebSocketServer
{
public enum ServerLogLevel { Nothing, Subtle, Verbose };
public delegate void ClientConnectedEventHandler(WebSocketConnection sender, EventArgs e);
public class WebSocketServer
{
#region private members
private string webSocketOrigin; // location for the protocol handshake
private string webSocketLocation; // location for the protocol handshake
#endregion
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static IPEndPoint ipLocal;
public event ClientConnectedEventHandler ClientConnected;
/// <summary>
/// TextWriter used for logging
/// </summary>
public TextWriter Logger { get; set; } // stream used for logging
/// <summary>
/// How much information do you want, the server to post to the stream
/// </summary>
public ServerLogLevel LogLevel = ServerLogLevel.Subtle;
/// <summary>
/// Gets the connections of the server
/// </summary>
public List<WebSocketConnection> Connections { get; private set; }
/// <summary>
/// Gets the listener socket. This socket is used to listen for new client connections
/// </summary>
public Socket ListenerSocker { get; private set; }
/// <summary>
/// Get the port of the server
/// </summary>
public int Port { get; private set; }
public WebSocketServer(int port, string origin, string location)
{
Port = port;
Connections = new List<WebSocketConnection>();
webSocketOrigin = origin;
webSocketLocation = location;
}
/// <summary>
/// Starts the server - making it listen for connections
/// </summary>
public void Start()
{
// create the main server socket, bind it to the local ip address and start listening for clients
ListenerSocker = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
ipLocal = new IPEndPoint(IPAddress.Loopback, Port);
ListenerSocker.Bind(ipLocal);
ListenerSocker.Listen(100);
LogLine(DateTime.Now + "> server stated on " + ListenerSocker.LocalEndPoint, ServerLogLevel.Subtle);
ListenForClients();
}
// look for connecting clients
private void ListenForClients()
{
ListenerSocker.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
private void OnClientConnect(IAsyncResult asyn)
{
byte[] buffer = new byte[1024];
string headerResponse = "";
// create a new socket for the connection
var clientSocket = ListenerSocker.EndAccept(asyn);
var i = clientSocket.Receive(buffer);
headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
//Console.WriteLine(headerResponse);
if (clientSocket != null)
{
// Console.WriteLine("HEADER RESPONSE:"+headerResponse);
var key = headerResponse.Replace("ey:", "`")
.Split('`')[1] // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
.Replace("\r", "").Split('\n')[0] // dGhlIHNhbXBsZSBub25jZQ==
.Trim();
var test1 = AcceptKey(ref key);
var newLine = "\r\n";
var name = "Charmaine";
var response = "HTTP/1.1 101 Switching Protocols" + newLine
+ "Upgrade: websocket" + newLine
+ "Connection: Upgrade" + newLine
+ "Sec-WebSocket-Accept: " + test1 + newLine + newLine
+ "Testing lang naman po:" + name
;
// which one should I use? none of them fires the onopen method
clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(response));
}
// keep track of the new guy
var clientConnection = new WebSocketConnection(clientSocket);
Connections.Add(clientConnection);
// clientConnection.Disconnected += new WebSocketDisconnectedEventHandler(ClientDisconnected);
Console.WriteLine("New user: " + ipLocal);
// invoke the connection event
if (ClientConnected != null)
ClientConnected(clientConnection, EventArgs.Empty);
if (LogLevel != ServerLogLevel.Nothing)
clientConnection.DataReceived += new DataReceivedEventHandler(DataReceivedFromClient);
// listen for more clients
ListenForClients();
}
void ClientDisconnected(WebSocketConnection sender, EventArgs e)
{
Connections.Remove(sender);
LogLine(DateTime.Now + "> " + sender.ConnectionSocket.LocalEndPoint + " disconnected", ServerLogLevel.Subtle);
}
void DataReceivedFromClient(WebSocketConnection sender, DataReceivedEventArgs e)
{
Log(DateTime.Now + "> data from " + sender.ConnectionSocket.LocalEndPoint, ServerLogLevel.Subtle);
Log(": " + e.Data + "\n" + e.Size + " bytes", ServerLogLevel.Verbose);
LogLine("", ServerLogLevel.Subtle);
}
/// <summary>
/// send a string to all the clients (you spammer!)
/// </summary>
/// <param name="data">the string to send</param>
public void SendToAll(string data)
{
Connections.ForEach(a => a.Send(data));
}
/// <summary>
/// send a string to all the clients except one
/// </summary>
/// <param name="data">the string to send</param>
/// <param name="indifferent">the client that doesn't care</param>
public void SendToAllExceptOne(string data, WebSocketConnection indifferent)
{
foreach (var client in Connections)
{
if (client != indifferent)
client.Send(data);
}
}
/// <summary>
/// Takes care of the initial handshaking between the the client and the server
/// </summary>
private void Log(string str, ServerLogLevel level)
{
if (Logger != null && (int)LogLevel >= (int)level)
{
Logger.Write(str);
}
}
private void LogLine(string str, ServerLogLevel level)
{
Log(str + "\r\n", level);
}
private static string AcceptKey(ref string key)
{
string longKey = key + guid;
byte[] hashBytes = ComputeHash(longKey);
return Convert.ToBase64String(hashBytes);
}
static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
private static byte[] ComputeHash(string str)
{
return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
}
private void ShakeHands(Socket conn)
{
using (var stream = new NetworkStream(conn))
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
//read handshake from client (no need to actually read it, we know its there):
LogLine("Reading client handshake:", ServerLogLevel.Verbose);
string r = null;
while (r != "")
{
r = reader.ReadLine();
LogLine(r, ServerLogLevel.Verbose);
}
// send handshake to the client
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: " + webSocketOrigin);
writer.WriteLine("WebSocket-Location: " + webSocketLocation);
writer.WriteLine("");
}
// tell the nerds whats going on
LogLine("Sending handshake:", ServerLogLevel.Verbose);
LogLine("HTTP/1.1 101 Web Socket Protocol Handshake", ServerLogLevel.Verbose);
LogLine("Upgrade: WebSocket", ServerLogLevel.Verbose);
LogLine("Connection: Upgrade", ServerLogLevel.Verbose);
LogLine("WebSocket-Origin: " + webSocketOrigin, ServerLogLevel.Verbose);
LogLine("WebSocket-Location: " + webSocketLocation, ServerLogLevel.Verbose);
LogLine("", ServerLogLevel.Verbose);
LogLine("Started listening to client", ServerLogLevel.Verbose);
//conn.Listen();
}
}
}
Connection issue resolved, next would be SENDING DATA to client.
just change shakehands in WebSocketServer.cs file in solution with below code and your error will gone..
private void ShakeHands(Socket conn)
{
using (var stream = new NetworkStream(conn))
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
//read handshake from client (no need to actually read it, we know its there):
LogLine("Reading client handshake:", ServerLogLevel.Verbose);
string r = null;
Dictionary<string, string> headers = new Dictionary<string, string>();
while (r != "")
{
r = reader.ReadLine();
string[] tokens = r.Split(new char[] { ':' }, 2);
if (!string.IsNullOrWhiteSpace(r) && tokens.Length > 1)
{
headers[tokens[0]] = tokens[1].Trim();
}
LogLine(r, ServerLogLevel.Verbose);
}
//string line = string.Empty;
//while ((line = reader.ReadLine()) != string.Empty)
//{
// string[] tokens = line.Split(new char[] { ':' }, 2);
// if (!string.IsNullOrWhiteSpace(line) && tokens.Length > 1)
// {
// headers[tokens[0]] = tokens[1].Trim();
// }
//}
string responseKey = "";
string key = string.Concat(headers["Sec-WebSocket-Key"], "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
using (SHA1 sha1 = SHA1.Create())
{
byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(key));
responseKey = Convert.ToBase64String(hash);
}
// send handshake to the client
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: " + webSocketOrigin);
writer.WriteLine("WebSocket-Location: " + webSocketLocation);
//writer.WriteLine("Sec-WebSocket-Protocol: chat");
writer.WriteLine("Sec-WebSocket-Accept: " + responseKey);
writer.WriteLine("");
}
// tell the nerds whats going on
LogLine("Sending handshake:", ServerLogLevel.Verbose);
LogLine("HTTP/1.1 101 Web Socket Protocol Handshake", ServerLogLevel.Verbose);
LogLine("Upgrade: WebSocket", ServerLogLevel.Verbose);
LogLine("Connection: Upgrade", ServerLogLevel.Verbose);
LogLine("WebSocket-Origin: " + webSocketOrigin, ServerLogLevel.Verbose);
LogLine("WebSocket-Location: " + webSocketLocation, ServerLogLevel.Verbose);
LogLine("", ServerLogLevel.Verbose);
LogLine("Started listening to client", ServerLogLevel.Verbose);
//conn.Listen();
}
You may also take a look at the WebSocketRPC library which should be pretty simple to use, for both the "raw" connection and the RPC connections (the messages can also be mixed).
The following can be useful to you:
The code responsible for sending/receiving raw messages is located in the Connection class.
In the repository you can also find how a base JavaScript client is implemented.
Disclaimer: I am the author.

Categories

Resources