C# Socket Client: Understand and Manage messages - c#

I need to implement a Socket Client in C#.
The socket server is a software connected with my C# client through the port 3000.
Every message is composed as follow:
Some fields: 4 bytes
Message Length: 2 bytes
Some fields: 4 bytes
Message Index: 2 bytes
Some fields: depends on the 'Message Length' field
When the client Receive, the buffer could contain more the one message and repeated messages.
I have to split the content of the buffer in message, save the message in a list if that message is not already present in this list.
I understand that the message is already present in the list if the index of the message is in the list.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
namespace Client
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Socket sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000);
private void btnC_Click(object sender, EventArgs e)
{
sck.Connect(endPoint);
if (sck.Connected)
{
Form1.ActiveForm.Text = Form1.ActiveForm.Text + " - Connected";
}
byte[] buffer = new byte[255];
int rec;
while (true)
{
buffer = new byte[255];
rec = sck.Receive(buffer, 0, buffer.Length, 0);
Array.Resize(ref buffer, rec);
/* Understand and Manage the messages */
}
}
}
}
Have you some suggestion to implement the correct code to understand and manage the received messages ??
Thanks in advance!
Fabio

You can implement your message like that :
public abstract class NetworkMessage
{
private List<byte> _buffer;
protected abstract void InternalDeserialize(BinaryReader reader);
protected NetworkMessage()
{
_buffer = new List<byte>();
}
public void Deserialize()
{
using (MemoryStream stream = new MemoryStream(_buffer.ToArray()))
{
BinaryReader reader = new BinaryReader(stream);
this.InternalDeserialize(reader);
}
}
}
public class YourMessage : NetworkMessage
{
public int YourField
{
get;
set;
}
protected override void InternalDeserialize(BinaryReader reader)
{
YourField = reader.ReadInt32();
}
}
You have to care about non-blocking network. I mean if you're waiting (with an infinite time) to receive some data in a button event, you will block your client. There is a lot of tutorials about it :)
And i think that in your "1. Some fields 4 bytes" there is an Id for each messages. You can eventually create a Dictionnary<id, Networkmessage> which will return the good message by your id (if you know a bit about reflection, you can create a Func to generate your message) or just a switch. I'm not sure I explained properly what i'm thinking.

Your scenario seems most-effectively addressed by wrapping the Socket in NetworkStream, which in turn may be wrapped by BinaryReader. Because you're using this in a Winforms program, you will also want to avoid blocking the UI thread, i.e. don't run the I/O itself in the btnC_Click() event handler method.
Unfortunately, the stock BinaryReader doesn't offer async methods, so the simplest solution is to use synchronous I/O but execute it in a Task.
Something like this might work for you (error handling omitted for clarity):
// Simple holder for header and data
class Message
{
public int Field1 { get; private set; }
public short Length { get; private set; }
public int Field2 { get; private set; }
public short Index { get; private set; }
public byte[] Data { get; private set; }
public Message(int field1, short length, int field2, int index, byte[] data)
{
Field1 = field1;
Length = length;
Field2 = field2;
Index = index;
Data = data;
}
}
private void btnC_Click(object sender, EventArgs e)
{
sck.Connect(endPoint);
// If Connect() completes without an exception, you're connected
Form1.ActiveForm.Text = Form1.ActiveForm.Text + " - Connected";
using (NetworkStream stream = new NetworkStream(sck))
using (BinaryReader reader = new BinaryReader(stream))
{
Message message;
while ((message = await Task.Run(() => ReadMessage(reader))) != null)
{
// process message here, preferably asynchronously
}
}
}
private Message ReadMessage(BinaryReader reader)
{
try
{
int field1, field2;
short length, index;
byte[] data;
field1 = reader.ReadInt32();
length = reader.ReadInt16();
field2 = reader.ReadInt32();
index = reader.ReadInt16();
// NOTE: this is the simplest implementation based on the vague
// description in the question. I assume "length" contains the
// actual length of the _remaining_ data, but it could be that
// the number of bytes to read here needs to take into account
// the number of bytes already read (e.g. maybe this should be
// "length - 20"). You also might want to create subclasses of
// Message that are specific to the actual message, and use
// the BinaryReader to initialize those based on the data read
// so far and the remaining data.
data = reader.ReadBytes(length);
}
catch (EndOfStreamException)
{
return null;
}
}
In general, asynchronous I/O would be better. The above is suitable for a relatively small number of connections. And given that this is the client side, it may well be you have just one connection. So the example I've offered will work fine. But do note that dedicating a single thread to each socket will scale very poorly, i.e. it can handle only a relatively small number of connections efficiently.
So keep that in mind if and when you find yourself coding a more elaborate socket scenario. In that case, you'll want to go to the extra trouble of implementing some kind of state-based mechanism that can accumulate data for each message as it comes in over raw byte receives, which you can do using NetworkStream.ReadAsync().
Finally, a couple of notes to address some of the comments you received for the question itself:
You definitely do want to use TCP here. UDP is unreliable – yes, it's message-based, but you also have to deal with the fact that a given message might be received more than once, messages may be received in a different order from that with which they were sent, and messages may not be received at all.
Yes, TCP is stream-oriented which means you have to impose your own message boundaries on it. But that's the only complication as compared to UDP, and otherwise UDP would be much more complicated to deal with (unless of course your code inherently does not need reliability…UDP is certainly good for some things, but it's definitely not the place to start for a new network programmer).
There is nothing about your question that suggests you need a "listening" socket. Only one end of a TCP connection needs to be listening; that is the server. You've specifically stated you're implementing the client, so listening is not needed. You just need to connect to the server (which your code does).

bool getHeader = True;
int header_size = 4+2+4+2;
int payload_size = 0;
byte[] header = new byte[header_size];
byte[] buffer = new byte[255];
int rec;
while (true)
{
if(getHeader)
{
rec = sck.Receive(header, 0, header_size, 0);
payload_size = ..... parse header[4] & header[5] to payload size (int)
getHeader = False;
}else{
rec = sck.Receive(buffer, 0, payload_size, 0);
getHeader = True;
}
}

Related

C# How to make a TCP server raise an event/callback when there is new data from the client

So basically I want my server to raise an event (or a callback) when a connected client sends data. I can't come up with a solution to this problem and can't find anything online after days of searching.
What I've thought of was making an asynchronous foreach loop that looped through all the connected users, and check if there is any data to be read on each one (using TcpClient.Avaliable, but a network stream could also check this) but an infinite loop like this without any stop would be bad practice and use an insane amount of resources (from what I understand at least, I am new to threading and networking).
There is logic I need to be executed whenever the server gets data from a client (in this case a message, because it's a chat application), basically broadcast it to every other user, but I just can't find out how to detect if any user has sent data so that it raises an event to broadcast the message, log the message, etc...
Please be "soft" with the explanations as I am new to threading/networking and ty in advance.
As per request here is my code, take note that it is prototype-y and a bit unfinished, but I'm sure it gets the point across:
//Properties
public List<User> ConnectedUsers { get; private set; } = new List<User>();
public TcpListener listener { get; set; }
public bool IsListeningForConnections { get; set; }
public int DisconnectionCheckInterval { get; set; } //in seconds
//Events
public event EventHandler<ServerEventArgs> UserConnected;
public event EventHandler<ServerEventArgs> MessageReceived;
public NetworkManager()
{
listener = new TcpListener(IPAddress.Parse("192.168.1.86"), 6000); //binds // TODO: Change to: user input / prop file
DisconnectionCheckInterval = 10;
IsListeningForConnections = false;
}
public async void StartListeningForConnections()
{
IsListeningForConnections = true;
listener.Start();
while (IsListeningForConnections)
{
User newUser = new User();
newUser.TcpClient = await listener.AcceptTcpClientAsync();
OnUserConnected(newUser); // raises/triggers the event
}
}
public void StartListeningForDisconnections()
{
System.Timers.Timer disconnectionIntervalTimer = new System.Timers.Timer(DisconnectionCheckInterval * 1000);
//TODO: setup event
//disconnectionIntervalTimer.Elasped += ;
disconnectionIntervalTimer.AutoReset = true;
disconnectionIntervalTimer.Enabled = true;
//disconnectionIntervalTimer.Stop();
//disconnectionIntervalTimer.Dispose();
}
public async void StartListeningForData()
{
//??????????
}
public async void SendData(string data, TcpClient recipient)
{
try
{
byte[] buffer = Encoding.ASCII.GetBytes(data);
NetworkStream stream = recipient.GetStream();
await stream.WriteAsync(buffer, 0, buffer.Length); //await
Array.Clear(buffer, 0, buffer.Length);
}
catch { } //TODO: handle exception when message couldn't be sent (user disconnected)
}
public string ReceiveData(TcpClient sender)
{
try
{
NetworkStream stream = sender.GetStream();
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
return Encoding.ASCII.GetString(buffer).Trim('\0');
}
catch
{
return null; //TODO: handle exception when message couldn't be read (user disconnected)
}
}
protected virtual void OnUserConnected(User user)
{
ConnectedUsers.Add(user);
UserConnected?.Invoke(this, new ServerEventArgs() { User = user });
}
protected virtual void OnMessageReceived(User user, Message message) //needs trigger
{
MessageReceived?.Invoke(this, new ServerEventArgs() { User = user, Message = message });
}
basically a different class will call all the 3 classes that start with "StartListeningForX", then one of the 3 corresponding events are raised when one of the checks goes through (disconnection/connection/new message), and process that data, I just can't get my hands on how to call an event when a new message arrives for each user.
What I've thought of was making an asynchronous foreach loop that looped through all the connected users, and check if there is any data to be read on each one (using TcpClient.Avaliable, but a network stream could also check this) but an infinite loop like this without any stop would be bad practice and use an insane amount of resources
The standard practice is to have an "infinite" loop for each connected client, so that there is always a read going on every socket. I put "infinite" in quotes because it will actually eventually stop; either by reading 0 bytes (indicating end of stream) or by receiving an exception (indicating a broken connection).
I am new to threading/networking
It's funny how often I see developers trying to learn networking and threading at the same time. Let me be clear: threading and TCP/IP sockets are both extremely complicated and take quite a bit of time to learn all the sharp corners. Trying to learn both of these topics at once is insane. I strongly recommend choosing one of them to learn about (I'd recommend threading first), and only after that one is mastered, proceed to the other.
RabbitMQ
If you have access to the client side code, I'd consider using something like RabbitMQ, or a similar queue service. This allows to link the different apps together through a message broker or queue, and get messages/events real time.
There are functions you can call on event received.

How to ignore packets that doesn't have a valid OpCode

So I'm currently learning about networking in terms of the relationship between a server and it's clients. And as far as I understand, the server starts listening for incoming connections and once a connection has been established, packets will be sent back and forth.
Here is my take on it, I like to visualize it in a way of how a game would do it.
So you have your server running and then you connect with your client, with the connection comes a Login packet, the server would then parse this packet and continue the login process and the client would eventually login to the game world.
Packets are structured in a certain way to easily keep track of what type of packet it is by using an OpCode, the length of the packet and the actual payload.
So something like this.. [OpCode(2bytes), Length(4bytes), Payload(xBytes)]
And the server would then expect this format.
Issue
What if there was a malicious user who were to send a packet that doesn't match that packet format? I would have to dump that entire packet that the malicious user sent, but I don't know how big it is so I don't know how much to dump, leaving me at a spot prone for server crashes.
Question
How do I dump/ignore a packet that doesn't have a valid OpCode.. A Valid OpCode is an OpCode that exists in the OpCode enum
enum OpCode : short
{
Connect = 0
}
The way I'm doing it now is that I start the server by initializing the packets
Packets = new Dictionary<OpCode, IPacket>();
Packets.Add(OpCode.Connect, new ConnectPacket());
and calling a method named Run
public void Run()
{
_listener = new TcpListener(IPAddress.Any, Constants.PORT);
_listener.Start(10);
_isRunning = true;
Console.WriteLine("Server has started..");
while (_isRunning)
{
_clients.Add(new Connection(_listener.AcceptTcpClient(), Packets));
}
}
When a TcpClient has been accepted, it enters the constructor
private TcpClient _socket;
private Dictionary<OpCode, IPacket> _packets;
public NetReader Reader;
public Connection(TcpClient client, Dictionary<OpCode, IPacket> packets)
{
_buffer = new byte[bufferSize];
_socket = client;
_packets = packets;
Reader = new NetReader(_socket.GetStream());
Console.WriteLine($"Client connected: {_socket.Client.RemoteEndPoint}");
ProcessPackets();
}
And as you can see I call the ProcessPackets function which checks if the first two bytes evaluates to a valid OpCode, if so, process the rest of the data that follows, if not I guess I want to dump the data and read the next packet.
Same goes for if the malicious packet somehow gets the OpCode right, if that's the case then dump the packet because the rest of the packet will probably be corrupt.
public void ProcessPackets()
{
Task.Run(() =>
{
IPacket packet;
while (true)
{
OpCode = Reader.ReadInt16();
if (_packets.TryGetValue((OpCode)OpCode, out packet))
{
_packets[(OpCode)OpCode].Process(this);
}
else
{
//Dump the packet
}
}
});
}
And here is an example of what a Process function could look like, but it doesn't matter that much since I want to solve dumping the packet if the OpCode isn't valid first.
public void Process(Connection connection)
{
var length = connection.Reader.ReadInt32();
var payload = connection.Reader.ReadBytes(length);
Console.WriteLine($"Received and processed Connection Packet");
}
NetReader is just a class that derives from BinaryReader
class NetReader : BinaryReader
{
private NetworkStream _ns;
private byte[] _tempBuffer;
public NetReader(NetworkStream ns) : base(ns)
{
_ns = ns;
_tempBuffer = new byte[8];
}
public override byte ReadByte()
{
byte val = (byte)_ns.ReadByte();
return val;
}
public override short ReadInt16()
{
int val = _ns.Read(_tempBuffer, 0, 2);
return BitConverter.ToInt16(_tempBuffer, 0)
}
public override int ReadInt32()
{
int val = _ns.Read(_tempBuffer, 0, 4);
return BitConverter.ToInt32(_tempBuffer, 0)
}
public override byte[] ReadBytes(int count)
{
byte[] bytes = new byte[count];
_ns.Read(bytes, 0, count);
return bytes;
}
}

C# Transferring multiple data types over a single port (receiving)

The Problem
The other day I had an "amazing" idea for my C# Application. Instead of using one port for each data type (text, image, file), I would like to use a single port for all three data types. I am very close to implementing this.
So far I've got a TcpListener listening on port 23722. When a TcpClient connects I start writing incoming data to a file using StreamWriter.Write(datareceived); and data comes in a specific pattern (each line represents an image, a text message or a file):
dnett{This is a sample text message.) dneti{NLtqmuvtGBdDY546 ... NLtqmuvtGBdDY546} dnetf{Example.exe,NLtqmuvtGBdDY546 ... NLtqmuvtGBdDY546}
As you can see from above images and files are converted to Base64 before sending. To convert them back I would use byte[] tmp = Convert.FromBase64String(string), but the problem is that I can't read the file line by line while the data is still incoming (being written with StreamWriter). The other problem is, that I don't know which data has already been processed (example: which files had already been written to the filesystem).
What I need is:
a solution for reading/writing file at the same time
knowing which data has already been processed
OR
another way of doing this (different approach)
Thanks
By the way I am only 15 yrs old and English is not my first language, so I am sorry for potentially stupid question and for mistakes in the above question asked.
Usually, you would first send the size of the "message" and then the actual "message" (from the server). You could extend this to first send the size of your message type, then send your message type, then send the size of the actual message and then send that message.
To read back, you'd do something like this
using System;
using System.IO;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using Newtonsoft.Json;
using Unity3DRemoteRendererClient.Helpers;
namespace Unity3DRemoteRendererClient.Communications.TCPCommunication {
public sealed partial class TCPClientManager {
public sealed class Receiver {
private NetworkStream _stream;
private Thread _thread;
//Subscribe to this event if you want an event ever time data arrives.
public event EventHandler < UnityToClientMessage > DataReceivedEvent;
private static ManualResetEvent ShutDownEvent = new ManualResetEvent(false);
public void Start(NetworkStream stream) {
_stream = stream;
_thread = new Thread(Run);
_thread.Start();
}
private void Run() {
try {
// ShutdownEvent is a ManualResetEvent signaled by
// Client when its time to close the socket.
while (!ShutDownEvent.WaitOne(0)) {
try {
if (!_stream.DataAvailable) continue;
//Read the first 4 bytes which represent the size of the message, and convert from byte array to int32
var sizeinfo = new byte[4];
_stream.Read(sizeinfo, 0, 4);
var messageSize = BitConverter.ToInt32(sizeinfo, 0);
//create a new buffer for the data to be read
var buffer = new byte[messageSize];
var read = 0;
//Continue reading from the stream until we have read all bytes #messageSize
while (read != messageSize) {
read += _stream.Read(buffer, read, buffer.Length - read);
}
//I use flatbuffers, so you should deserialize yourself.
var message = new UnityToClientMessage().FlatDeserialize(buffer);
//raise data received event
OnDataReceived(message);
} catch (IOException ex) {
// Handle the exception...
throw;
}
}
} catch (Exception ex) {
// Handle the exception...
throw;
} finally {
_stream.Close();
}
}
private void OnDataReceived(UnityToClientMessage e) {
EventHandler < UnityToClientMessage > handler = DataReceivedEvent;
if (handler != null) {
handler(this, e);
}
}
public void ShutDown() {
ShutDownEvent.Set();
}
}
}
}
Just modify it to first read the size of the message type message and then to read the actual message. You can do a switch statement once you have the message describing the type of message coming, and process it accordingly.
i'm beginner too. But i think that you can use an ArrayList to converse all lines until the all lines are readed and sended. So when you are ready and the file is all readed yoy can save it . If you are asking how to know recognize which kind of files is sended first send a file you can send a caracter or a string that allow the other program that wait file to recognize whic file is going to be revived. Excuse me for my English. I hope that I could be helpful for you.

Why is my message divided into multiple frames?

When i send a request to my server or a reply to my client, the message i send is always divided into multiple parts.
So i need to call Receive multiple times to get to the last part/frame.
Why does this happen.. is there a better way of sending and receiving an xml encoded string?
This is the code of my client:
private void SendRequestAsyncTaskStart<T>(object contextObj, T request)
{
ZmqContext context = (ZmqContext)contextObj;
ZmqSocket requestSocket = CreateServerSocket(context);
SerializeAndSendRequest(request, requestSocket);
}
private ZmqSocket CreateServerSocket(ZmqContext context)
{
var client = context.CreateSocket(SocketType.REQ);
client.Connect(_requestReplyEndpoint);
client.Linger = TimeSpan.Zero;
client.ReceiveReady += PollInReplyHandler;
return client;
}
public static string Serialize(this object obj)
{
string result;
using (var memoryStream = new MemoryStream())
{
using (var reader = new StreamReader(memoryStream))
{
var serializer = new DataContractSerializer(obj.GetType());
serializer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
result = reader.ReadToEnd();
}
}
return result;
}
This is the code of my server:
private void ListenForRequestsThreadStart(object contextObj)
{
ZmqContext context = (ZmqContext)contextObj;
using (
ZmqSocket frontend = context.CreateSocket(SocketType.REP),
backend = context.CreateSocket(SocketType.DEALER))
{
string bindAddress = string.Format("tcp://*:{0}", _listenForRequetsPort);
frontend.Bind(bindAddress);
backend.Bind("inproc://backend");
frontend.ReceiveReady += HandleRequestReceived;
// polling
}
}
private void HandleRequestReceived(object sender, SocketEventArgs e)
{
string message;
bool hasNext;
do
{
message = socket.Receive(Encoding.ASCII);
hasNext = socket.ReceiveMore;
} while (hasNext);
// after calling Receive 3 times i get my actual message
}
Since you're sending via a socket you're at the mercy of the network. First, the network will have broken your message down in multiple packates each of which is received separately by your listener. Every now and then, the underlying socket on the listening machine will say to itself 'Got some incoming, but there's more to come. Wait a bit'. After a while it'll say, 'Oh well, give what I've got' and keep waiting'.
That's what's happening. In WCF, the WCF implementation gets its data via sockets which do exactly the same thing. But WCF waits till the whole message arrives before giving it to your waiting code. That's one of the advantages of using a Framework like WCF. It protects you from the metal.
Any message sent over TCP may be divided into several packets depending on its size. That's why you should never assume to get a message in one go, but read until you're sure you've received everything.

Threading a Synchronous XNA Network

I have been doing several hours of research on a topic that I thought would've been very trivial. So far I've come up empty handed and wanted to see what you guys think. I'm currently messing with XNA (which is actually quite irrelevant now that I think about it) building a client/server architecture for a "game" project. Really nothing more than playing around with networking, animations, etc.
Anyway I've read quite a bit about the differences between Synchronous and Asynchronous networks and the viability of threading synchronous applications to simulate asynchronous behavior and have decided to do just that. Now I know my code isn't pretty but I'm just testing right now. Here's how it's (sort of) set up:
Game is run in the main thread.
On initialization->Connect to server.
Send x position of player class's sprite object to server.
Server receives, acknowledges with a print to console and sends the same data back.
Data is read into a logfile.
I've begun work on a messenger class that will eventually read (or sniff) the packets coming from the server and dispatch them accordingly making draw/update calls as needed. My problem is that I cant figure out how to properly thread the connection method to have that run (and then block?) separate from the Send/Receive loop (or what I'd like to be a continuous loop).
Like I said I'm no expert, I'm just doing this for fun so I may be all over the place. Anyway here are the essentials code-wise:
Networking.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
namespace DrawTest
{
class Networking
{
public void StartClient(Messenger m)
{
// Data buffer for incoming data.
StreamWriter _con = new StreamWriter("data.txt");
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// This example uses port 11000 on the local computer.
IPHostEntry ipHostInfo = Dns.Resolve("127.0.0.1");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress,3000);
// Create a TCP/IP socket.
Socket sender = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Connect the socket to the remote endpoint. Catch any errors.
try {
sender.Connect(remoteEP);
_con.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());
while (m.isAlive)
{
m.SocketStream(sender, _con);
}
if (Messenger.mPacket() == "close_socket")
{
_con.WriteLine("Connection closed by client.");
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}
} catch (ArgumentNullException ane) {
_con.WriteLine("ArgumentNullException : {0}",ane.ToString());
} catch (SocketException se) {
_con.WriteLine("SocketException : {0}",se.ToString());
} catch (Exception e) {
_con.WriteLine("Unexpected exception : {0}", e.ToString());
}
_con.Flush();
}
catch (Exception e) {
_con.WriteLine(e.ToString());
_con.Flush();
}
}
}
}
Messenger.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace DrawTest
{
public class Messenger
{
byte[] bytes = new byte[1024];
byte[] incBuffer = new byte[1024];
public bool isAlive = true;
Vector2 position = new Vector2(0.0f, 0.0f);
public Vector2 getPos()
{
return position;
}
public void setPos(Vector2 pos)
{
position = pos;
}
public void SocketStream(Socket s, StreamWriter logfile)
{
byte[] msg = null;
int bytesSent = 0;
int bytesRec = 0;
msg = BitConverter.GetBytes(position.X);
// Encode the data string into a byte array.
bytesSent = s.Send(msg);
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
//logfile.WriteLine(Messenger.mDecoder(incBuffer, bytesRec));
}
public string mDecoder(byte[] msg, int size)
{
string DecodedMessage;
byte[] bytes = new byte[1024];
DecodedMessage = Encoding.ASCII.GetString(msg, 0, size);
if (DecodedMessage == "close_socket")
{
isAlive = false;
return DecodedMessage;
}
return DecodedMessage;
}
public static string mPacket()
{
return null;
}
}
}
Think that should do it. The other code is relatively self-explanatory (abstract player/sprite classes and the typical XNA Game.cs)
Thanks in advance for any help!
You may do something along the lines of:
public void SendData(Socket s)
{
byte[] msg = null;
int bytesSent = 0;
msg = BitConverter.GetBytes(position.X);
// Encode the data string into a byte array.
bytesSent = s.Send(msg);
}
void ReceiveData(Socket s)
{
int bytesExpected = 1024; // somehow specify the number of bytes expected
int totalBytesRec = 0; // adds up all the bytes received
int bytesRec = -1; // zero means that you're done receiving
while(bytesRec != 0 && totalBytesRec < bytesExpected )
{
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
totalBytesRec += bytesRec;
}
}
Back in your StartClient class, you should start your receive thread first then send the data:
// Start your receive thread first
Thread t = new Thread(()=>{ReceiveData(sender);});
t.IsBackground = true;
t.Start();
// Then send the data
SendData(sender);
// Wait for the thread to terminate (if you need to)
t.Join(30000);
// Once you close the socket, then it will throw an exception
// in the receive thread (which you should catch) and you can
// exit the thread, thus terminating the thread.
This is roughly how you would start a thread that performs the receive.
Update (based on comments)
I would recommend that you take a look at some of the Patterns for Multithreaded Network Server in C#.
The server side should start a new thread for every client connection accepted and a "connection handler" should take over and manage the sending/receiving of data from there on:
while(serverRunning)
{
Socket clientSocket = serverSocket.Accept();
// You can write your own connection handler class that automatically
// starts a new ReceiveData thread when it gets a client connection
ConnectionHandler chandler = new ConnectionHandler(clientSocket);
// Have an on-client-disconnected event which you can subscribe to
// and remove the handler from your list when the client is disconnected
chandler.OnClinetDisconnectedEvent += new OnClientDisconnectedDelegate(OnClientDisconnected);
mHandlerList.Add(chandler);
}
// When you're terminating the program, then just go through
// the list of active ConnectionHandlers and call some method
// which tells them to close their connections with the clients
// and terminates the thread.
To be even more precise, you are likely to have the very similar behavior with the client's and the server's ReceiveData method: i.e. synchronously send a message back whenever they receive some message. Here is a more realistic example that might help you conceptualize it better:
void ReceiveData(Socket s)
{
int bytesExpected = 1024; // somehow specify the number of bytes expected
int totalBytesRec = 0; // adds up all the bytes received
int bytesRec = -1; // zero means that you're done receiving
while(bytesRec != 0 && totalBytesRec < bytesExpected )
{
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
totalBytesRec += bytesRec;
if(needToReply)
{
// Send another message
SendData(s);
}
}
}
This is a long running thread, of course, so you would generally like to have it run for as long as the player is connected to the internet. The comment about closing the connection and terminating the thread is specifically for the situation where you need to have a graceful exit (i.e. the player quits the game or the rare case that server needs to be shut down).

Categories

Resources