我使用TcpListener
和TcpClient
设置了服务器和客户端。
我想将一个对象发送到我的服务器应用程序进行处理。
我发现了using System.Runtime.Serialization
和以下的documentation,但我不想四处寻找我正在以长篇大论的方式做这件事。
问题:通过TCP流处理和发送对象的最佳方法是什么?
发送和接收。
以下是我的对象示例:
// Create a new house to send
house newHouse = new house();
// Set variables
newHouse.street = "Mill Lane";
newHouse.postcode = "LO1 BT5";
newHouse.house_number = 11;
newHouse.house_id = 1;
newHouse.house_town = "London";
答案 0 :(得分:7)
假设您有一个类House
(在您的连接的两侧都可用),如下所示:
[Serializable]
public class House
{
public string Street { get; set; }
public string ZipCode { get; set; }
public int Number { get; set; }
public int Id { get; set; }
public string Town { get; set; }
}
您可以将班级序列化为MemoryStream
。然后,您可以在TcpClient
连接中使用,如下所示:
// Create a new house to send house and set values.
var newHouse = new House
{
Street = "Mill Lane",
ZipCode = "LO1 BT5",
Number = 11,
Id = 1,
Town = "London"
};
var xmlSerializer = new XmlSerializer(typeof(House));
var networkStream = tcpClient.GetStream();
if (networkStream.CanWrite)
{
xmlSerializer.Serialize(networkStream, newHouse);
}
当然,你必须做一些调查才能让程序毫无例外地运行。 (例如,检查memoryStream.Length
不要大于int,a.s.o。),但我希望我给你正确的建议,以帮助你的方式; - )
答案 1 :(得分:2)
您的回答意味着以下对象(通常使用驼峰案例命名类):
[Serializable]
class House:ISerializable
{
public string Street {get; set;}
public string PostalCode {get; set;}
public int HouseNumber {get; set;}
public int HouseID {get; set;}
public string City {get; set;}
public House() { }
protected House(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new System.ArgumentNullException("info");
Street = (string)info.GetValue("Street ", typeof(string));
PostalCode = (string)info.GetValue("PostalCode", typeof(string));
HouseNumber = (int)info.GetValue("HouseNumber", typeof(int));
HouseID = (int)info.GetValue("HouseID", typeof(int));
City = (string)info.GetValue("City", typeof(string));
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand,
Flags=SecurityPermissionFlag.SerializationFormatter)]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new System.ArgumentNullException("info");
info.AddValue("Street ", Street);
info.AddValue("PostalCode", PostalCode);
info.AddValue("HouseNumber", HouseNumber);
info.AddValue("HouseID", HouseID );
info.AddValue("City", City);
}
}
现在您可以序列化对象:
void Send(Stream stream)
{
BinaryFormatter binaryFmt = new BinaryFormatter();
House h = new House()
{
Street = "Mill Lane",
PostalCode = "LO1 BT5",
HouseNumber = 11,
HouseID = 1,
City = "London"
};
binaryFmt.Serialize(stream, h);
}
答案 2 :(得分:2)
您只需使用House
属性装饰[Serializable]
课程即可。 (您不需要定义其他答案中公布的所有其他内容)
然后,您可以使用BinaryFormatter
类对其进行序列化,从而在线上发送此对象。
您是否考虑过设置WCF服务而不是使用TcpListener和TcpClient?让生活更轻松。
例如,您可以定义一个返回房屋的服务
[ServiceContract]
public interface IService
{
[OperationContract]
House GetHouse(int houseId);
}
参见this现实世界的例子。
答案 3 :(得分:1)
如何将xml House流反序列化回接收端的House对象? 我正在参考Fischermaen的回答中给出的解决方案。
在我的接收结束时,我可以使用以下内容在输出窗口中看到字符串表示:
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
提前谢谢。
编辑 *
好的,这个解决方案对我有用。可能需要一些整理。
这是一种反序列化字符串的方法:
public static T DeserializeFromXml<T>(string xml)
{
T result;
XmlSerializer ser = new XmlSerializer(typeof(T));
using (TextReader tr = new StringReader(xml))
{
result = (T)ser.Deserialize(tr);
}
return result;
}
然后从我的TPC / IP接收结束我调用这样的方法:
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
House house = DeserializeFromXml<House>(encoder.GetString(message, 0, bytesRead));
//Send Message Back
byte[] buffer = encoder.GetBytes("Hello Client - " + DateTime.Now.ToLongTimeString());
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
tcpClient.Close();
}
答案 4 :(得分:0)
首先创建一个空的ServerApplication和ClientApplication作为控制台应用程序来简化示例。
然后,将可序列化对象的定义放入单独的程序集中,然后将共享程序集的引用添加到每个项目(服务器和客户端)。必要性是否共享同一个对象,而不仅仅是相同的类副本。
生成DLL &gt; 解决方案资源管理器中解决方案'ServerApplication'中的右键&gt;添加新项目... - &gt;选择类库 (例如,将此项目命名为 MySharedHouse ) 将默认的Class1重命名为House并完成它
[Serializable]
public class House
{
public string Street { get; set; }
public string ZipCode { get; set; }
public int Number { get; set; }
public int Id { get; set; }
public string Town { get; set; }
}
MySharedHouse和Build中的正确clic。
现在dll已构建,我们需要在Server Project和Client Project中添加它。 ServerApplication中的右clic&gt;添加参考&gt;浏览并找到dll,例如
项目\ ServerApplication \ MySharedHouse \ BIN \调试\ MySharedHouse.dll
使用相同的dll(相同路径)在ClientApplication中重复此过程。
现在,您可以将ServerApplication和ClientApplication中的House类实例用作单个对象,只需在顶部添加句子“使用MySharedHouse”。
服务器代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using MySharedHouse;
namespace ServerApplication
{
class Program
{
static void Main(string[] args)
{
MessageServer s = new MessageServer(515);
s.Start();
}
}
public class MessageServer
{
private int _port;
private TcpListener _tcpListener;
private bool _running;
private TcpClient connectedTcpClient;
private BinaryFormatter _bFormatter;
private Thread _connectionThread;
public MessageServer(int port)
{
this._port = port;
this._tcpListener = new TcpListener(IPAddress.Loopback, port);
this._bFormatter = new BinaryFormatter();
}
public void Start()
{
if (!_running)
{
this._tcpListener.Start();
Console.WriteLine("Waiting for a connection... ");
this._running = true;
this._connectionThread = new Thread
(new ThreadStart(ListenForClientConnections));
this._connectionThread.Start();
}
}
public void Stop()
{
if (this._running)
{
this._tcpListener.Stop();
this._running = false;
}
}
private void ListenForClientConnections()
{
while (this._running)
{
this.connectedTcpClient = this._tcpListener.AcceptTcpClient();
Console.WriteLine("Connected!");
House house = new House();
house.Street = "Evergreen Terrace";
house.ZipCode = "71474";
house.Number = 742;
house.Id = 34527;
house.Town = "Springfield";
_bFormatter.Serialize(this.connectedTcpClient.GetStream(), house);
Console.WriteLine("send House!");
}
}
}
}
客户代码
using System;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using MySharedHouse;
namespace ClientApplication
{
class Program
{
static void Main(string[] args)
{
MessageClient client = new MessageClient(515);
client.StartListening();
}
}
public class MessageClient
{
private int _port;
private TcpClient _tcpClient;
private BinaryFormatter _bFormatter;
private Thread _listenThread;
private bool _running;
private House house;
public MessageClient(int port)
{
this._port = port;
this._tcpClient = new TcpClient("127.0.0.1", port);
this._bFormatter = new BinaryFormatter();
this._running = false;
}
public void StartListening()
{
lock (this)
{
if (!_running)
{
this._running = true;
this._listenThread = new Thread
(new ThreadStart(ListenForMessage));
this._listenThread.Start();
}
else
{
this._running = true;
this._listenThread = new Thread
(new ThreadStart(ListenForMessage));
this._listenThread.Start();
}
}
}
private void ListenForMessage()
{
Console.WriteLine("Reading...");
try
{
while (this._running)
{
this.house = (House)this._bFormatter.Deserialize(this._tcpClient.GetStream());
Console.WriteLine(this.house.Street);
Console.WriteLine(this.house.ZipCode);
Console.WriteLine(this.house.Number);
Console.WriteLine(this.house.Id);
Console.WriteLine(this.house.Town);
}
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
}
}
Wooala!第一个通过TCP / IP发送的房子