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;
}
}
Related
I've written a TcpClient and Server which are communicating via an SslStream.
The communication works, but when i send a message from the Client to the Server, first the Server reads 1 Byte, and in the next step the rest. Example: I want to send "test" via Client, and the Server receives first "t", and then "est"
Here is the code for the Client to send
public void Send(string text) {
byte[] message = Encoding.UTF8.GetBytes(text);
SecureStream.BeginWrite(message, 0, message.Length, new AsyncCallback(WriteCallback), null);
}
private void WriteCallback(IAsyncResult AR) {
}
And here the code the Server uses to read
private SslStream CryptedStream = ...;
private byte[] buffer = new byte[1024];
public void BeginReadCallback(IAsyncResult AsyncCall) {
// initialize variables
int bytesRead = 0;
try {
// retrieve packet
bytesRead = CryptedStream.EndRead(AsyncCall);
// check if client has disconnected
if (bytesRead > 0) {
// copy buffer to a temporary one
var temporaryBuffer = buffer;
Array.Resize(ref temporaryBuffer, bytesRead);
string read = Encoding.ASCII.GetString(temporaryBuffer);
SetText(read);
// read more data
CryptedStream.BeginRead(buffer, 0, 1024, new AsyncCallback(BeginReadCallback), null);
// client is still connected, read data from buffer
//ProcessPacket(temporaryBuffer, temporaryBuffer.Length, helper);
} else {
// client disconnected, do everything to disconnect the client
//DisconnectClient(helper);
}
} catch (Exception e) {
// encountered an error, closing connection
// Program.log.Add(e.ToString(), Logger.LogLevel.Error);
// DisconnectClient(helper);
}
}
Did i miss something?
Thanks for your help
As Lasse explained streaming APIs do not promise you to return a specific number of bytes per read.
The best fix for this is to not use sockets. Use a higher level API such as WCF, SignalR, HTTP, ...
If you insist you probably should use BinaryReader/Writer to send your data. That makes it quite easy. For example, it has string sending built-in. You also can manually length-prefix easily with those classes.
Probably, you don't need async IO and should not use it. If you insist you can at least get rid of the callbacks by using await.
I'm trying to send multiple files over TCP using C# TcpClient, for a file below 64kB it works great, but when I have more it throws an exeption that host-computer disconnected.
Here is my code:
Server side(sending).
1) SocketServer class
public abstract class SocketServer
{
private Socket serverSocket;
public SocketServer(IPEndPoint localEndPoint)
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(localEndPoint);
serverSocket.Listen(0);
serverSocket.BeginAccept(BeginAcceptCallback, null);
}
private void BeginAcceptCallback(IAsyncResult ar)
{
Socket clientSocket = serverSocket.EndAccept(ar);
Console.WriteLine("Client connected");
ClientConnection clientConnection = new ClientConnection(this, clientSocket);
Thread clientThread = new Thread(new ThreadStart(clientConnection.Process));
clientThread.Start();
serverSocket.BeginAccept(BeginAcceptCallback, null);
}
internal abstract void OnReceiveMessage(ClientConnection client, byte header, byte[] data);
}
Here goes ClientConnection:
public class ClientConnection
{
private SocketServer server;
private Socket clientSocket;
private byte[] buffer;
private readonly int BUFFER_SIZE = 8192;
public ClientConnection(SocketServer server, Socket clientSocket)
{
this.server = server;
this.clientSocket = clientSocket;
buffer = new byte[BUFFER_SIZE];
}
public void Process()
{
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.Peek, BeginReceiveCallback, null);
}
private void BeginReceiveCallback(IAsyncResult ar)
{
int bytesReceived = clientSocket.EndReceive(ar);
if (bytesReceived >= 4)
{
clientSocket.Receive(buffer, 0, 4, SocketFlags.None);
// message size
int size = BitConverter.ToInt32(buffer, 0);
// read message
int read = clientSocket.Receive(buffer, 0, size, SocketFlags.None);
// if data still fragmented, wait for it
while (read < size)
{
read += clientSocket.Receive(buffer, read, size - read, SocketFlags.None);
}
ProcessReceivedData(size);
}
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.Peek, BeginReceiveCallback, null);
}
private void ProcessReceivedData(int size)
{
using (PacketReader pr = new PacketReader(buffer))
{
// message header = 1 byte
byte header = pr.ReadByte();
// next message data
byte[] data = pr.ReadBytes(size - 1);
server.OnReceiveMessage(this, header, data);
}
}
public void Send(byte[] data)
{
// first of all, send message length
clientSocket.Send(BitConverter.GetBytes(data.Length), 0, 4, SocketFlags.None);
// and then message
clientSocket.Send(data, 0, data.Length, SocketFlags.None);
}
}
Implementation of FileServer class:
public class FileServer : SocketServer
{
string BinaryPath;
public FileServer(IPEndPoint localEndPoint) : base(localEndPoint)
{
BinaryPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
}
internal override void OnReceiveMessage(ClientConnection client, byte hdr, byte[] data)
{
// Convert header byte to ENUM
Headers header = (Headers)hdr;
switch(header)
{
case Headers.Queue:
Queue(client, data);
break;
default:
Console.WriteLine("Wrong header received {0}", header);
break;
}
}
private void Queue(ClientConnection client, byte[] data)
{
// this message contains fileName
string fileName = Encoding.ASCII.GetString(data, 1, data.Length - 1);
// combine path with assembly location
fileName = Path.Combine(BinaryPath, fileName);
if (File.Exists(fileName))
{
FileInfo fileInfo = new FileInfo(fileName);
long fileLength = fileInfo.Length;
// pass the message that is now start a file transfer, contains:
// 1 byte = header
// 16 bytes = file length
using (PacketWriter pw = new PacketWriter())
{
pw.Write((byte)Headers.Start);
pw.Write(fileLength);
client.Send(pw.GetBytes());
}
//
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
int read = 0, offset = 0;
byte[] fileChunk = new byte[8191];
while (offset < fileLength)
{
// pass message with file chunks, conatins
// 1 byte = header
// 8195 bytes = file chunk
using (PacketWriter pw = new PacketWriter())
{
fs.Position = offset;
read = fs.Read(fileChunk, 0, fileChunk.Length);
pw.Write((byte)Headers.Chunk);
pw.Write(fileChunk, 0, read);
client.Send(pw.GetBytes());
}
offset += read;
}
}
}
}
}
And helper classes:
public class PacketWriter : BinaryWriter
{
private MemoryStream memoryStream;
public PacketWriter() : base()
{
memoryStream = new MemoryStream();
OutStream = memoryStream;
}
public byte[] GetBytes()
{
byte[] data = memoryStream.ToArray();
return data;
}
}
public class PacketReader : BinaryReader
{
public PacketReader(byte[] data) : base(new MemoryStream(data))
{
binaryFormatter = new BinaryFormatter();
}
}
Client have the almost same code. Except receive:
internal override void OnReceiveMessage(ServerConnection client, byte hdr, byte[] data)
{
Headers header = (Headers)hdr;
switch(header)
{
case Headers.Start:
Start(data); // save length of file and prepare for receiving it
break;
case Headers.Chunk:
Chunk(data);
break;
default:
Console.WriteLine("Wrong header received {0}", header);
break;
}
}
private void Chunk(byte[] data)
{
// Process reveived data, write it into a file
}
Do TCP sockets automatically close after 64kB send?
No, TCP sockets do not automatically close after 64 KB of sent data, nor after any amount of sent data. They remain open until either end closes the connection or a network error of some sort happens.
Unfortunately, your question is extremely vague, providing practically no context. But I will point out one potentially serious bug in your client code: you are not restricting the data read by the client to that which you expect for the current file. If the server is sending more than one file, it is entirely possible and likely that on reaching the end of the data for the current file, a read containing that final sequence of bytes will also contain the initial sequence of bytes for the next file.
Thus, the expression totalBytesRead == fileLenght will fail to ever be true and your code will attempt to read all of the data the server sends as if it's all part of the first file.
One easy way to check for this scenario is to look at the size of the file that was written by the client. It will be much larger than expected, if the above is the issue.
You can fix the code by only ever receiving at most the number of bytes you actually expect to be remaining:
while ((bytesRead = binaryReader.Read(
buffer, 0, Math.Min(fileLenght - totalBytesRead, buffer.Length))) > 0)
{
fs.Write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
if (totalBytesRead == fileLenght)
{
break;
}
}
While you're at it, you might fix the spelling of the variable named fileLenght. I know Intellisense makes it easy to type the variable name whether it's spelled right or not, but if you one day have to grep through the source code looking for code that involves itself with "length" values, you'll miss that variable.
EDIT:
Having pointed out the problem in the code you posted with your question originally, I now find myself looking at completely different code, after your recent edit to the question. The example is still not a good, minimal, complete code example and so advice will still have to be of a limited nature. But I can see at least two major problems in your receive handling:
You are mixing Receive() and BeginReceive(). This is probably not too terrible in general; what makes it really bad in your case is that in the completion handler for the initial asynchronous receive, you are blocking the thread while you use the synchronous Receive() to (attempt to) receive the rest of the data, and then to make matters worse, in the FileServer case you continue to block that thread while you transmit the file data to the client.I/O completion handlers must not block the thread for any significant amount of time; they must retrieve the data for the completed operation, and return as quickly as is feasible. There's a lot of wiggle room, but not enough to justify tying up the IOCP thread for the entire file transfer.
While the first issue is bad, it's probably not bad enough to actually cause the connection to reset. In a low-volume scenario, and especially if you're just dealing with one client at a time at the moment, I doubt blocking the IOCP thread that's being used to call your I/O completion callback will cause any easily observable problems.What's much worse is that you allocate the buffer array once, with a length of 8192, and then proceed to attempt to stuff the entire counted-byte stream of bytes being sent. On the assumption that the client will be sending short messages, this is probably fine on the server side. But on the client side, the value of size can easily be greater than the 8192 bytes allocated in the buffer array.I find it very likely that your BeginReceiveCallback() method is throwing an ArgumentOutOfRangeException after receiving the first 8192 bytes, due to the fact that you are passing the Receive() method a size value that exceeds the length of the buffer array. I can't prove that, because the code example is incomplete. You'll have to follow up on that theory yourself.
As long as I'm reviewing the code you posted, some other irregularities stood out as I scanned through it:
It's a bit odd to be using a buffer length of 8191 when transmitting the file.
Your BeginReceiveCallback() method discards whatever bytes were sent initially. You call EndReceive(), which returns the count of bytes received, but the code that follows assumes that the size value in the stream is still waiting to be read, when it very easily could have been sent in the first block of bytes. Indeed, your comparison bytesReceived >= 4 seems to be intended to refrain from processing received data until you have that length value. Which is wrong for another reason:
If your header is 1 byte followed by a 4-byte count-of-bytes value, then you really need bytesReceived > 4. But worse, TCP is allowed to drip out a single byte at a time if it really wants to (it won't, but if you write your code correctly, it won't matter). Since you only process received data if you get at least 4 bytes, and since if you don't get 4 bytes, you just ignore whatever was sent so far, you could theoretically wind up ignoring all of the data that was sent.
That you have the risk that any data could be ignored at all is terrible. But #2 above guarantees it, and #3 just adds insult to injury.
When receiving the size value, you are reading a 32-bit integer, but when sending the size value (i.e. for the file itself), you are writing a 64-bit integer. You'll get the right number on the receiving end, because BinaryWriter uses little-endian and that means that the first four bytes of any 64-bit integer less than 2^31 are identical to the four bytes of the 32-bit integer representation of the same number. But you'll still wind up with four extra zero bytes after that; if treated as file data as they likely would, that would corrupt your file.
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;
}
}
I've been messing around with TCP sockets in C#, and I'm having some trouble communicating with an FTP server I have set up. I can connect initially and get the 220 message, but when I send the "USER nrcrast" command, I never get a response, and the DataAvailable property returns false. Anybody know what I'm doing wrong? Here's my code so far:
namespace TCPClient
{
public partial class TCPClientForm : Form
{
private TcpClient myClient;
NetworkStream stream;
public TCPClientForm()
{
InitializeComponent();
send();
}
void send()
{
while (true)
{
try
{
myClient = new TcpClient("nrcrast.dyndns.info", 21);
break;
}
catch (SocketException ex)
{
Console.WriteLine(ex.ToString());
}
}
stream = myClient.GetStream();
int sendOffset = 0;
int recOffset=0;
int dataLength;
Byte[] receiveData = new Byte[256];
// wait for a response
dataLength = stream.Read(receiveData, recOffset, receiveData.Length);
String recvdMessage = System.Text.Encoding.ASCII.GetString(receiveData, 0, dataLength);
Console.WriteLine(recvdMessage.ToString());
recOffset+=dataLength;
String message = "USER nrcrast";
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
stream.Write(data, 0, data.Length);
sendOffset += data.Length;
// wait for a response
while (!stream.DataAvailable)
{
}
dataLength = stream.Read(receiveData, 0, receiveData.Length);
recvdMessage = System.Text.Encoding.ASCII.GetString(receiveData, 0, dataLength);
Console.WriteLine(recvdMessage.ToString());
}
}
A shoot in the dark, you need to put a carriage return and new line at the end of the command
String message = "USER nrcrast\r\n";
If you're interested in looking over someone's shoulder on a similar project (not saying its perfect), I did the same thing a long time ago (this was back in .net 1.1, and ported it to .net 2.0 when the ssl stream stuff was added).
there are some tricky pieces to the FTP protocols with respect to timings of when you send commands, when the server expects you to open the data connection, when you read the server response, and so forth (depending on active / passive modes).
anyway, feel free to look over My FTP Client Library source code for reference as you do your own implementation. it's a pretty complete implementation and does auth ssl/tls as well.
I've developing (or trying to, anyway) a program that uses Asynchronous Socket to, supposedly, pass strings to and fro the server and client, at any time.
This program requires no more than one client be connected to a server. I tried Socket Programming, but I found out it blocks the program until either one receives something.
Since I have only a basic understanding of Asynchronous socket programming, I just went for the simplest one I could find, or at least, the simplest one I could understand.
Here's my code for the Server:
public Socket g_server_conn;
public byte[] g_bmsg;
public bool check = false;
private void net_As_Accept(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
g_server_conn = server_conn.EndAccept(iar);
g_bmsg = new byte[1024];
check = true;
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
private void net_As_Send(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndSend(iar);
}
private void net_As_Receive(IAsyncResult iar)
{
try
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndReceive(iar);
if (g_bmsg.Length != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
public void net_Data_Send(string msg2snd) // Function for sending through socket
{
MessageBox.Show(msg2snd);
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg2snd);
g_server_conn.BeginSend(byData, 0, byData.Length, SocketFlags.None, new AsyncCallback(net_As_Send), g_server_conn);
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
private void net_Data_Receive(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { net_Data_Receive(txt); }));
else
lblBuffer.Text = txt;
if (txt.StartsWith("&"))
{
// Do something
}
}
And here's my code for the Client:
private void net_As_Connect(IAsyncResult iar)
{
try
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndConnect(iar);
g_bmsg = new byte[1024];
check = true;
string toSendData = "&" + net_Name;
net_Data_Send(toSendData);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
private void net_As_Send(IAsyncResult iar)
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndSend(iar);
}
private void net_As_Receive(IAsyncResult iar)
{
if (g_bmsg.Length != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
public void net_Data_Send(string msg2snd)
{
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg2snd);
g_client_conn.BeginSend(byData, 0, byData.Length, SocketFlags.None, new AsyncCallback(net_As_Send), g_client_conn);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
private void net_Data_Receive(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { net_Data_Receive(txt); }));
else
lblBuffer.Text = txt;
if (txt.StartsWith("&"))
{
// Do Something
}
else if (txt.StartsWith("$"))
{
// Do something Else
}
}
Now, the Client can connect to the Server fine. The Client can even send in a string containing the user's name to the Server, which will then be displayed on the Server. The Server then sends out the name of its user to the Client, which the client receives and displays. Whatever is sent is stored in a Label (lblBuffer)
But afterwards, say I have the following code:
private void btnSendData_Click(object sender, EventArgs e)
{
string posMov = "Stuff to send";
net_Data_Send(posMov);
}
The Client receives nothing. Putting a Message Box in net_Data_Send(msg2snd) function reveals that the server does in fact send out the message. In fact, putting in the Message Box in that function makes it work (the Client receives it), for reasons I don't know. Since I haven't tried sending a message from the Client to the Server (other than the name when the Client Connects), I assume the Client will have the same problem sending to the Server.
Also, when it does send the second message (by putting a Message Box in the net_Data_Send function), only parts of the Label (lblBuffer) are overwritten. So if I my name is "Anon E. Moose", and the Server sends that when the Client connects, and I try to send out, say, "0.0" (via button press) the Label on the Client would then read "0.0n E. Moose".
What did I do wrong? Can I have some help on this, please?
Perhaps I have a problem with net_Data_Receive and net_Data_Send?
I think you need to call BeginReceive on your client again, it looks like you are only calling it once, so after it has received the server name, it isn't listening for any more data from the server
private void net_As_Receive(IAsyncResult iar)
{
var bytesRead = g_client_conn.EndReceive(iar);
if (bytesRead != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, bytesRead));
check = false;
}
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
also, as I mentioned in my comment, use the bytesRead value to work out how much of the buffer you need to use.
You will need to work out if the data you have received from the socket is the full amount, or if you need to read more data to make up the current message from the other side.
BeginReceive doesn't just call its callback whenever a new packet (string in your case arrives). In fact. BeginReceive or any raw socket method works in a stream based fasion, not packet based. See http://msdn.microsoft.com/en-us/library/bew39x2a.aspx for an example.
What you need to do, is in your 'net_As_Receive' callback method (naming is terrible imo), you need to make a call first to socket.EndRecieve(IAsyncResult), which in turn returns the total bytes currently available. After that, you have to make a decision whether to receive more data or not.
For example:
private StringBuilder packetBuilder;
{
if (packetBuilder == null)
packetBuilder = new StringBuilder();
// finalyze the receive
int length = g_server_conn.EndReceive(iar);
if (length != 0)
{
// get the total bytes received. Note that the length property is of that of the number of bytes received rather than that of the buffer
packetBuilder.Append(Encoding.ASCII.GetString(g_bmsg, 0, length));
net_Data_Receive(packetBuilder.ToString());
check = false;
}
// receive the next part
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
Note that this example doesnt care about packages. It will work if your lucky but there is a good change either a part of a string will be shown or 2 different strings will be combined. A good implementation will look for a string end and only show that part while buffering the rest untill a new string end is found. You can also use a StreamReader for making your life much easier