Reputation: 73
First off, I have read many guides and posts on StackOverflow and elsewhere and have still not solved this. I have been trying to learn about sockets recently and it is proving to be a challenge. I have understood how to use sockets with strings etc etc but I know want to start sending objects so I can better organise my program. I am making a chat client; the server will connect multiple clients, it will work a little like IRC I guess. I have made the very basics of the server; I have methods to check what packet type was received (all packets inherit from Packet
) and have a very basic switch statement that takes action based on the packet received. Before this can be done I obviously need to convert the byte[]
received in the socket to a Packet
. The issue comes when I am attempting to de-serialize the byte array. I get a SerilizationException on the line where I call .Deserialize(); Here is the text:
System.Runtime.Serialization.SerializationException was unhandled by user code HResult=-2146233076 Message=Unable to find assembly 'Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
I don't get why this is happening; I have tried writing different ways of de-serializing the array, added random if statements to make sure I am not passing an empty array (which I am not) but can not get this to work for the life of me. I am hoping someone could point out what I an doing wrong? Am I going about sending objects via sockets all wrong? Have I simply missed something basic out from my code?
I have included the barebones of my client (all it does is send a MessagePacket
with a string at the moment and my server is also included. Now, this program is not the prettiest by any means so I apologize in advance; this is all a learning curve for me!
Server:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Chat_Application.Packets;
namespace Chat_Application
{
class Server
{
private const int port = 1090;
private readonly ManualResetEvent allDone;
private readonly Dictionary<String, Socket> connections; // will hold all client sockets
private readonly IPAddress ipAddress;
private readonly IPEndPoint ipEndPoint;
private readonly Thread listenThread; // seperate thread to run the server
private readonly Socket serverSocket;
public Server()
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
connections = new Dictionary<string, Socket>();
ipAddress = IPAddress.Parse(GetLocalIPv4(NetworkInterfaceType.Ethernet));
ipEndPoint = new IPEndPoint(ipAddress, port);
listenThread = new Thread(StartListen);
allDone = new ManualResetEvent(false);
}
//called to start the new thread
public void Start()
{
listenThread.Start();
}
//TODO: implement this method
public void Stop()
{
throw new NotImplementedException();
}
//method that is run on the new server thread
public void StartListen()
{
serverSocket.Bind(ipEndPoint);
serverSocket.Listen(20);
String statusUpdate = "\n<INFO> Socket bound, listening for connections...";
Program.mainWin.AddConsoleText(statusUpdate);
while (true)
{
allDone.Reset();
serverSocket.BeginAccept(AcceptConnectionAsync, serverSocket);
allDone.WaitOne();
}
}
//Asynchronus method to recieve connections. The logic of the way things are done is not great, it is only temporary while I learn better ways to do things
public void AcceptConnectionAsync(IAsyncResult ar)
{
var bufferBytes = new byte[1024];
Packet packet = null;
allDone.Set();
var listener = (Socket) ar.AsyncState;
var client = listener.EndAccept(ar);
client.Receive(bufferBytes);
packet = ConvertPacket(bufferBytes);
switch (CheckPacketType(packet))
{
case PacketType.Message:
packet = ConvertPacket(bufferBytes);
var messagePacket = (MessagePacket) packet;
String userMessage = "(" + messagePacket.userName + ") " +
Encoding.UTF8.GetString(messagePacket.message);
Program.mainWin.AddConsoleText(userMessage);
break;
case PacketType.Connection:
packet = ConvertPacket(bufferBytes);
var connectionPacket = (Connect) packet;
String connectionMessage = "\n<CONNECT>New connection established to " +
connectionPacket.userName;
connections.Add(connectionPacket.userName, client);
Program.mainWin.AddConsoleText(connectionMessage);
break;
case PacketType.Command:
// TODO Implement this case, add new packet type
packet = ConvertPacket(bufferBytes);
var commandPacket = (MessagePacket) packet;
break;
case PacketType.Disconnect:
packet = ConvertPacket(bufferBytes);
var disconnectPacket = (Dissconnect) packet;
String disconnectMessage = "\n<DISCONNECT>User " + disconnectPacket.userName +
" Disconnected succesffuly";
connections.Remove(disconnectPacket.userName);
if (disconnectPacket.goodDissconnect)
{
Program.mainWin.AddConsoleText(disconnectMessage);
}
else
{
String message = "\n<DISCONNECT>User " + disconnectPacket.userName +
" Disconnected unexpectedly";
Program.mainWin.AddConsoleText(message);
}
break;
case PacketType.Request:
// TODO Implement this case, add new packet type
packet = ConvertPacket(bufferBytes);
var requestPacket = (MessagePacket) packet;
break;
case PacketType.Default:
MessageBox.Show("Packet Error", "Empty Packet Recieved");
break;
default:
break;
}
}
//used to check what type of packet has been recieved
private PacketType CheckPacketType(Packet packet)
{
switch (packet.type)
{
case PacketType.Message:
return PacketType.Message;
break;
case PacketType.Connection:
return PacketType.Connection;
break;
case PacketType.Command:
return PacketType.Command;
break;
case PacketType.Disconnect:
return PacketType.Disconnect;
break;
case PacketType.Request:
return PacketType.Request;
break;
default:
break;
}
return PacketType.Default;
}
//converts the byte array to a packet
private Packet ConvertPacket(byte[] bytes)
{
Packet packet = null;
//try to convert byte array to a Packet, the error occurs on the last line; when trying to de-serialize
/*try
{*/
var memStream = new MemoryStream();
var binForm = new BinaryFormatter();
memStream.Write(bytes, 0, bytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
packet = (Packet) binForm.Deserialize(memStream);
//}
/*catch (Exception) // MAKE THIS THE APPROPRIATE EXCEPTION
{
MessageBox.Show("An unexpected error has occured when atempting to convert a packet!");
}*/
if (packet != null)
{
return packet;
}
else
{
MessageBox.Show("Error converting packet!");
return null;
}
}
//gets the local ip
public string GetLocalIPv4(NetworkInterfaceType _type)
{
var output = "";
foreach (var item in NetworkInterface.GetAllNetworkInterfaces())
{
if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up)
{
foreach (var ip in item.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
output = ip.Address.ToString();
}
}
}
}
return output;
}
}
}
Client:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Chat_Application.Packets;
namespace Client
{
public partial class ClientWindow : Form
{
public ClientWindow()
{
InitializeComponent();
}
private void ClientWindow_Load(object sender, EventArgs e)
{
MessagePacket packet = new MessagePacket("Chris", "Hello Server!");
byte[] data;
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(new IPEndPoint(IPAddress.Parse(GetLocalIPv4(NetworkInterfaceType.Ethernet)), 1090));
packet.type = PacketType.Message;
data = ConvertPacket(packet);
//to check if the packet has been converted correctly
if (data != null)
{
socket.Send(data);
}
}
public string GetLocalIPv4(NetworkInterfaceType _type)
{
string output = "";
foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
{
if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up)
{
foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
output = ip.Address.ToString();
}
}
}
}
return output;
}
private byte[] ConvertPacket(Packet packet)
{
byte[] bytes = new byte[1024];
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
formatter.Serialize(ms, packet);
bytes = ms.ToArray();
}
return bytes;
}
}
}
Upvotes: 1
Views: 889
Reputation: 70701
The exception seems pretty clear to me. When you are trying to deserialize the data, the Client
assembly that was referenced when the data was serialized is apparently not found.
Unfortunately, your code example does not include information about where and how you are declaring the Packet
class. But based on your reported behavior, it seems likely to me that you have linked the same .cs file to two different projects, with the result that each assembly winds up with its own private copy of the Packet
type.
If that's the case, then there are at least a couple of good solutions to the problem:
Client.dll
assembly, and then add the Client.dll
assembly as a reference for the Server.dll
assembly. Or you could build a third assembly containing the type, and have both your Client.dll
and Server.dll
assemblies reference that third assembly.XmlSerializer
instead. The XML won't include type information that requires a specific assembly to define the type in question. This is more commonly used when using XmlSerializer
to map to and from XML that is processed outside of .NET (i.e. where sharing a common assembly to define a type isn't possible), but this approach could address your specific issue also.Personally, I'd go with option #1, and put the type in a DLL shared by both client and server.
Upvotes: 2