I have list of ipaddress. I am using udp protocol to send request to these addressses asynchronously to an snmp agent. The available snmp agent on these address reply. When I call BeginSendTo with these addresses, devices are replying in random order. When ReceiveBeginReceiveFrom is called and stateobject object is constructed. The RemoteIPEndPoint doesn't belong to the machine that replied instead it belongs to other machine.
Is my approach of calling BeginSendTo is correct?
If Yes, y stateobject instance doesn't give me correct remoteendpoint that replied?
I am posting my code. Please correct if case something is missing.
public class Test
{
Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
class StateObject
{
public Socket Socket { get; set; }
public byte[] DataBuff { get; set; }
public IPEndPoint RemoteEndPoint { get; set; }
//validation are omited
public static StateObject Create(Socket s,byte[] dataBuf,IPEndPoint endpoint)
{
return new StateObject() { Socket = s, DataBuff = dataBuf, RemoteEndPoint = endpoint };
}
public static StateObject Transform(object objectstate)
{
return objectstate as StateObject;
}
}
public void Fx()
{
//list of ip address from 190.188.191.1 - 190.188.191.100
var address = new[] {
IPAddress.Parse("190.188.191.1"),
IPAddress.Parse("190.188.191.100")
};
byte[] dataGram = new byte[1024];
foreach (IPAddress item in address)
{
udpSocket.BeginSendTo(dataGram,
0,
dataGram.Length,
SocketFlags.None,
new IPEndPoint(item, 161),
SendComplete,
StateObject.Create(udpSocket, null, new IPEndPoint(item, 161))
);
}
}
private void SendComplete(IAsyncResult ar)
{
StateObject obj = StateObject.Transform(ar.AsyncState);
obj.Socket.EndSendTo(ar);//ignore the no of bytes send for now
byte[] receivedBytes = new byte[1024 * 4];//assume 4kb is enough for response
var ep = obj.RemoteEndPoint as EndPoint;
obj.Socket.BeginReceiveFrom(receivedBytes,
0,
receivedBytes.Length,
SocketFlags.None,
ref ep,
ReceivedData,
StateObject.Create(obj.Socket, receivedBytes, obj.RemoteEndPoint)
);
}
private void ReceivedData(IAsyncResult ar)
{
StateObject obj = StateObject.Transform(ar.AsyncState);
var ep = obj.RemoteEndPoint as EndPoint;
//response received from ip 190.188.191.2 but in stateobject.remoteendpoint will give 190.188.191.1
var bytesReceived = obj.Socket.EndReceiveFrom(ar,ref ep);
byte[] data = new byte[bytesReceived];
Array.Copy(obj.DataBuff,data, bytesReceived);
}
}
Finally i figured it out to solve this issue of fanning out multiple udp requests. New field introduced in State Type called "ID". When generating ip address a unique identifier is generated. This identifier is assigned to State Type instance and cached. Also this ID is sent in request inorder to identify the response from a specific IP address and retrieve its state value from its cache.
Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
Dictionary<int, StateObject> _Cache = new Dictionary<int, StateObject>();
class StateObject
{
public int ID { get; set; }//uniquely identify the response
public Socket Socket { get; set; }
public byte[] DataBuff { get; set; }
public IPEndPoint RemoteEndPoint { get; set; }
//validation are omited
public static StateObject Create(Socket s, byte[] dataBuf, IPEndPoint endpoint)
{
return new StateObject() { Socket = s, DataBuff = dataBuf, RemoteEndPoint = endpoint };
}
public static StateObject Transform(object objectstate)
{
return objectstate as StateObject;
}
}
//This function is used to generate range of address and corresponding identifiers
IEnumerable<KeyValuePair<IPAddress, int>> GenerateMaps(IPAddress start, IPAddress end)
{
int count = -1;
yield return new KeyValuePair<IPAddress, int>(start, ++count);
//other address in the range
yield return new KeyValuePair<IPAddress, int>(end, ++count);
}
Also instead of receiving from particular host BeginReceiveFrom, instead i replaced with BeginReceive function that is capable of receiving and host. Mapping of identifier helped solve the issue in a cache.
Related
This question already has an answer here:
Async method in lock-statement block
(1 answer)
Closed 4 years ago.
Hello i have the following problem:
I have a class Pool that contains a list of Connection-s.The Connection is a wrapper over a socket.I somehow need to create the socket ,ConnectAsync-it ,wrap it into a Connection and return it to the caller.The problem is that i need this this collection to be thread-safe.Specifically i need the collection to be thread safe when i create a new Connection or when a Connection calls Pool-s Free method.
What alternative to the lock do i have? I have seen so far SemaphoreSlim but i do not understand it.
Pool
internal partial class Pool {
public static Pool MakePool(UserSettings settings)
{
return new Pool(settings);
}
private List<Connection> liveConnections;
private readonly object #lock = new object();
public readonly UserSettings settings;
public async Task<Connection> ConnectAsync()
{
Connection readyConnection;
lock(#lock)
{
if (this.liveConnections == null)
{
this.liveConnections = new List<Connection>(this.settings.MIN);
}
readyConnection = this.liveConnections.FirstOrDefault(x => !x.IsUsed);
if (readyConnection == null)
{
readyConnection = await CreateConnectionAsync(settings);
this.liveConnections.Add(readyConnection);
}
return readyConnection;
}
}
private async Task<Connection> CreateConnectionAsync(UserSettings settings)
{
//Socket initialization
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress address=IPAddress.Parse(settings.HOST_NAME);
int port = settings.PORT;
IPEndPoint point = new IPEndPoint(address, port);
await socket.ConnectAsync(point);
ConnectionSettings conSettings = new ConnectionSettings
{
pool = this,
ConnectionID = GenerateID(),
socket = socket,
};
Connection con= Connection.CreateConnection(conSettings);
return con;
}
//this gets called by the connection !!
internal void Free(string ID)
{
lock (#lock)
{
Connection con=this.liveConnections.Find(x => x.ID == ID);
con.IsUsed = false;
}
}
private static string GenerateID()=>Guid.NewGuid().ToString();
private Pool(UserSettings settings)
{
this.settings = settings;
}
}
Connection
public class Connection :IDisposable
{
private PhysicalConnection rawConnection;
internal static Connection CreateConnection(ConnectionSettings settings)
{
Connection con = new Connection(settings);
return new Connection(settings);
}
public readonly string ID;
private readonly Pool parentPool;
public bool IsUsed { get; internal set; }
public void Dispose()
{
this.parentPool.Free(this.ID);
}
private Connection(ConnectionSettings settings)
{
this.ID = settings.ConnectionID;
this.parentPool = settings.pool;
this.rawConnection = new PhysicalConnection(settings.socket);
}
}
ConnectionSettings
class ConnectionSettings
{
public Pool pool;
public string ConnectionID;
public Socket socket;
}
As you can see the Pool is sent in the Connection constructor so that the Connection can notify the Pool when it is disposed !
It looks like you don't even need to keep your call to CreateConnectionAsync inside the lock:
public async Task<Connection> ConnectAsync()
{
Connection readyConnection;
lock(#lock)
{
if (this.liveConnections == null)
{
this.liveConnections = new List<Connection>(this.settings.MIN);
}
readyConnection = this.liveConnections.FirstOrDefault(x => !x.IsUsed);
}
if (readyConnection == null)
{
readyConnection = await CreateConnectionAsync(settings);
lock(#lock)
{
this.liveConnections.Add(readyConnection);
}
}
return readyConnection;
}
Your CreateConnectionAsync does not use liveConnections collection at all. At the point you've done with searching through the collection, so it can be unlocked while your new connection asyncronously tries to connect to its endpoint.
You can use a pair of ConcurrentBags, one for used and one for unused connections. Initialise them in the constructor and I think you won't need any locks at all.
My application sends and receives TCP strings. One of those strings is to make an object visible depending on the string. My current code gives System.NullReferenceException has been thrown Object reference not set to an instance of an object.
I can't find the right method to access, I guess as RunOnUI doesn't seem to access this right.
My current code:
Listener class: (Snippet of the full class, I can post more if needed).
public void ReadCallback(IAsyncResult ar)
{
MainActivity ma = new MainActivity();
String content = string.Empty;
StateObject so = (StateObject)ar.AsyncState;
Socket handle = so.workSocket;
int bytesRead = handle.EndReceive(ar);
if (bytesRead > 0)
{
so.sb.Append(System.Text.Encoding.ASCII.GetString(so.buffer, 0, bytesRead));
content = so.sb.ToString();
}
if (content.IndexOf("0") > -1)
{
ma.RunOnUiThread(() =>
{
//This is where the received data is passed
ma.SortData(content);
});
Send(handle, content);
}
else
{
handle.BeginReceive(so.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), so);
}
}
And the code for SortData:
public void SortData(string data)
{
if(data == "0")
{
txtmsg.Visibility = ViewStates.Invisible;
}
} //This is the only code that's called, the txtmsg.Visibility part is the error producer once called.
You have to pass the Context to your class in a constructor
so you would add a constructor to your class like :
MainActivity mContext;
public YourClass(MainActivity context) {
this.mContext = context;
}
and then call the method from your class like :
mContext.RunOnUiThread(() =>
{
//This is where the received data is passed
mContext.SortData(content);
});
I have to listen to several udp sockets and receive multicast datagrams. After receiving from some socket, data has to be sent to the TransformBlock. I don't want to copy received data for each received packet, so data buffer is sent to the TransformBlock by reference. And that's why each particular socket has to wait for the TransformBlock to finish processing before it can begin reading to it's buffer again.
I used code from this article for async operations with sockets.
Also I need an ability to add or delete sockets which to listen during the program execution.
My multicast listener class:
using ChannelWithDecoder = Tuple<FastChannel, TransformBlock<SocketAsyncEventArgs, List<Message>>>;
class MulticastReceiveManager
{
const int MaxPacketSize = 1400;
readonly ConcurrentDictionary<int, SocketReadData> socketDataByPort = new ConcurrentDictionary<int, SocketReadData>();
public MulticastReceiveManager(IEnumerable<ChannelWithDecoder> channelsWithDecoders)
{
foreach (ChannelWithDecoder tuple in channelsWithDecoders) AddChannel(tuple.Item1, tuple.Item2);
}
static Socket CreateSocket(FastChannel channel)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
var end = new IPEndPoint(IPAddress.Any, channel.Port);
socket.Bind(end);
//подписываемся на source specific multicast
var membershipAddresses = new byte[12]; // 3 IPs * 4 bytes (IPv4)
IPAddress mcastGroup = IPAddress.Parse(channel.IP);
IPAddress mcastSource = IPAddress.Parse(channel.SourceIP);
Buffer.BlockCopy(mcastGroup.GetAddressBytes(), 0, membershipAddresses, 0, 4);
Buffer.BlockCopy(mcastSource.GetAddressBytes(), 0, membershipAddresses, 4, 4);
Buffer.BlockCopy(IPAddress.Any.GetAddressBytes(), 0, membershipAddresses, 8, 4);
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddSourceMembership, membershipAddresses);
return socket;
}
public void AddChannel(FastChannel channel, TransformBlock<SocketAsyncEventArgs, List<Message>> decoderTransformBlock)
{
var args = new SocketAsyncEventArgs();
args.SetBuffer(new byte[MaxPacketSize], 0, MaxPacketSize);
var socketAwaitable = new ReceiveSocketAwaitable(args);
Socket socket = CreateSocket(channel);
var socketReadData = new SocketReadData(socket, socketAwaitable, decoderTransformBlock);
if (!socketDataByPort.TryAdd(channel.Port, socketReadData))
throw new ArgumentException("Channel with port number = " + channel.Port + " already exists in dictionary. IP = " + channel.IP);
}
public void DeleteChannel(FastChannel channel)
{
SocketReadData socketReadData;
if (!socketDataByPort.TryRemove(channel.Port, out socketReadData))
throw new ArgumentException("Channel with port number = " + channel.Port + " could not be removed from dictionary. IP = " + channel.IP);
}
public async Task StartListen(CancellationToken token)
{
SocketReadData socketReadData = socketDataByPort.Values.First();
while (!token.IsCancellationRequested)
{
await socketReadData.Socket.ReceiveAsync(socketReadData.Awaitable);
SocketAsyncEventArgs args = socketReadData.Awaitable.EventArgs;
if (args.BytesTransferred > 0) await socketReadData.DecoderTransformBlock.SendAsync(args, token);
}
}
}
Socket data:
class SocketReadData
{
public Socket Socket { get; }
public ReceiveSocketAwaitable Awaitable { get; }
public TransformBlock<SocketAsyncEventArgs, List<Message>> DecoderTransformBlock { get; }
public SocketReadData(Socket socket, ReceiveSocketAwaitable awaitable, TransformBlock<SocketAsyncEventArgs, List<Message>> decoderTransformBlock)
{
Socket = socket;
Awaitable = awaitable;
DecoderTransformBlock = decoderTransformBlock;
}
}
And just in case, the code from the article for socket awaitable:
class ReceiveSocketAwaitable : INotifyCompletion
{
static readonly Action Sentinel = () => { };
Action mContinuation;
public bool WasCompleted { get; set; }
public SocketAsyncEventArgs EventArgs { get; }
public bool IsCompleted => WasCompleted;
public ReceiveSocketAwaitable(SocketAsyncEventArgs eventArgs)
{
Contract.Requires<ArgumentNullException>(eventArgs != null, "eventArgs can't be null");
EventArgs = eventArgs;
eventArgs.Completed += delegate
{
Action prev = mContinuation ?? Interlocked.CompareExchange(ref mContinuation, Sentinel, null);
prev?.Invoke();
};
}
public void OnCompleted(Action continuation)
{
if (mContinuation == Sentinel || Interlocked.CompareExchange(ref mContinuation, continuation, null) == Sentinel)
{
Task.Run(continuation);
}
}
public void Reset()
{
WasCompleted = false;
mContinuation = null;
}
public ReceiveSocketAwaitable GetAwaiter()
{
return this;
}
public void GetResult()
{
if (EventArgs.SocketError != SocketError.Success) throw new SocketException((int)EventArgs.SocketError);
//return EventArgs.BytesTransferred;
}
}
And extensions:
static class SocketExtensions
{
public static ReceiveSocketAwaitable ReceiveAsync(this Socket socket, ReceiveSocketAwaitable awaitable)
{
awaitable.Reset();
if (!socket.ReceiveAsync(awaitable.EventArgs)) awaitable.WasCompleted = true;
return awaitable;
}
public static ReceiveSocketAwaitable SendAsync(this Socket socket, ReceiveSocketAwaitable awaitable)
{
awaitable.Reset();
if (!socket.SendAsync(awaitable.EventArgs)) awaitable.WasCompleted = true;
return awaitable;
}
}
As you can see, this code achieves my goal for a single socket. In a loop it awaits for data and then await for transformblock to accept the packet. And only after that it can read data from a socket to the buffer again.
So the question is: how can i achieve this behavior for many sockets using async/await pattern, without creating threads and copying data for each packet?
I have several objects in my solution that use the following pattern:
#region Repositories
private RepositoryAccount _account;
private RepositoryInquiry _inquiry;
private RepositoryLoan _loan;
public RepositoryAccount Account { get { return _account ?? (_account = new RepositoryAccount(this)); } }
public RepositoryInquiry Inquiry { get { return _inquiry ?? (_inquiry = new RepositoryInquiry(this)); } }
public RepositoryLoan Loan { get { return _loan ?? (_loan = new RepositoryLoan(this)); } }
#endregion
I created a Generic Class to try to make the Property handling a little cleaner but ran into a snag. The Property Class looks like:
public class Property<T> where T : class, new()
{
private T _value;
public T Value
{
get { return _value ?? (_value = new T()); }
set
{
// insert desired logic here
_value = value;
}
}
public static implicit operator T(Property<T> value)
{
return value.Value;
}
public static implicit operator Property<T>(T value)
{
return new Property<T> { Value = value };
}
}
I replace my property (with backing objects) with the new Generic Property like:
public Property<RepositoryAccount> Account { get; set; }
public Property<RepositoryInquiry> Inquiry { get; set; }
public Property<RepositoryLoan> Loan { get; set; }
However, if you noticed in the original public getter, I need to instantiate each object with another object: this is required and there is no paramaterless constructor for these repository objects.
Any suggestions?
The whole object looks like:
public class UnitOfWorkSymitar : IUnitOfWork
{
#region Properties
internal IPHostEntry IpHostInfo;
internal IPAddress IpAddress;
internal IPEndPoint RemotEndPoint;
internal System.Net.Sockets.Socket Sender;
internal byte[] Bytes = new byte[1024];
internal bool IsAllowedToRun = false;
internal string SymServerIp;
internal int SymPort;
public string State { get; set; }
#endregion
#region Constructor
public UnitOfWorkSymitar() { OpenSymitarConnection(); }
protected UnitOfWorkSymitar(string serverIp, int? port) { OpenSymitarConnection(serverIp, port); }
#endregion
#region Implement IUnitOfWork
public void Commit() { /* No commit on this socket connection */ }
public void Rollback() { /* No rollback on this socket connection */ }
#endregion
#region Implement IDisposable
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
CloseSymitarConnection();
}
#endregion
#region Private Helpers
/// <summary>
/// Connect to a remote device.
/// </summary>
/// <returns>Success/failure message</returns>
private string OpenSymitarConnection(string serverIp = null, int? port = null)
{
var config = ConfigInfoSymitar.Instance;
SymServerIp = serverIp ?? config.SymServerIp;
SymPort = port ?? config.SymPort;
try
{
// Establish the remote endpoint for the socket.
//IpHostInfo = Dns.GetHostEntry(SymServerIp);
//IpAddress = IpHostInfo.AddressList[0];
IpAddress = Dns.GetHostAddresses(SymServerIp)[0];
RemotEndPoint = new IPEndPoint(IpAddress, SymPort);
// Create a TCP/IP socket.
Sender = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Connect the socket to the remote endpoint. Catch any errors.
Sender.Connect(RemotEndPoint);
}
catch (Exception ex)
{
State = DetermineError(ex);
return State;
//throw;
}
IsAllowedToRun = true;
State = "Success";
return State;
}
/// <summary>
/// Setup and send the request
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public string SendMessage(string request)
{
try
{
// Encode the data string into a byte array and add the carriage return for SymConnect.
var msg = Encoding.ASCII.GetBytes(request);
if(!request.EndsWith("\n"))
msg = Encoding.ASCII.GetBytes(request + "\n");
// Send the data through the socket.
var bytesSent = Sender.Send(msg);
// Receive the response from the remote device.
var bytesRec = Sender.Receive(Bytes);
var response = Encoding.ASCII.GetString(Bytes, 0, bytesRec);
response = response.Replace("\n", "");
return response;
}
catch (Exception ex)
{
return DetermineError(ex);
}
}
/// <summary>
/// Close the connection to a remote device.
/// </summary>
/// <returns></returns>
private string CloseSymitarConnection()
{
try
{
// Release the socket.
Sender.Shutdown(SocketShutdown.Both);
Sender.Close();
}
catch (Exception ex)
{
return DetermineError(ex);
}
finally
{
IsAllowedToRun = false;
}
return "Success!";
}
/// <summary>
/// Determine if this is an Arguments, Socket or some other exception
/// </summary>
/// <param name="ex">The exception to be checked</param>
/// <returns>String message</returns>
private static string DetermineError(Exception ex)
{
if (ex.GetType() == typeof(ArgumentNullException))
return "Missing Arguments: " + Environment.NewLine + ex.GetFullMessage();
if (ex.GetType() == typeof(SocketException))
return "Socket Error: " + Environment.NewLine + ex.GetFullMessage();
return "Unexpected Error: " + Environment.NewLine + ex.GetFullMessage();
}
#endregion
#region Symitar Samples
private static string ExecSymConnectRequest(string symServerIP, int symPort, string request)
{
// Data buffer for incoming data.
var bytes = new byte[1024];
// Connect to a remote device.
try
{
// Establish the remote endpoint for the socket.
// This example uses port 11000 on the local computer.
var ipHostInfo = Dns.GetHostEntry(symServerIP);
var ipAddress = ipHostInfo.AddressList[0];
var remoteEP = new IPEndPoint(ipAddress, symPort);
// Create a TCP/IP socket.
var sender = new System.Net.Sockets.Socket(AddressFamily.InterNetwork
, SocketType.Stream
, ProtocolType.Tcp);
// Connect the socket to the remote endpoint. Catch any errors.
try
{
sender.Connect(remoteEP);
// Encode the data string into a byte array and add the carriage return for SymConnect.
var msg = Encoding.ASCII.GetBytes(request + "\n");
// Send the data through the socket.
var bytesSent = sender.Send(msg);
// Receive the response from the remote device.
var bytesRec = sender.Receive(bytes);
var response = Encoding.ASCII.GetString(bytes, 0, bytesRec);
// Release the socket.
sender.Shutdown(SocketShutdown.Both);
sender.Close();
response.Replace("\n", "");
return response;
}
catch (ArgumentNullException ane)
{
return "Missing Arguments: " + ane.Message;
}
catch (SocketException se)
{
return "Socket Error: " + se.Message;
}
catch (Exception e)
{
return "Unexpected Error: " + e.Message;
}
}
catch (Exception e)
{
return e.Message;
}
}
#endregion
#region Repositories
private RepositoryAccount _account;
private RepositoryInquiry _inquiry;
private RepositoryLoan _loan;
public RepositoryAccount Account { get { return _account ?? (_account = new RepositoryAccount(this)); } }
public RepositoryInquiry Inquiry { get { return _inquiry ?? (_inquiry = new RepositoryInquiry(this)); } }
public RepositoryLoan Loan { get { return _loan ?? (_loan = new RepositoryLoan(this)); } }
#endregion
}
Might try something like:
class Program {
Property<string> Foo = new Property<string>(() => "FOO!");
}
public class Property<T> where T : class {
private Lazy<T> instance;
public Property(Func<T> generator) {
instance = new Lazy<T>(generator);
}
public T Value {
get { return instance.Value; }
}
public static implicit operator T(Property<T> value) {
return value.Value;
}
public static implicit operator Property<T>(T value) {
return new Property<T>(() => value);
}
}
There is a solution using reflection although it might not be the cleanest OOP one. A method of the FormatterService class in the System.Runtime namespace FormatterServices.GetUninitializedObject() will create an instance without calling the constructor. There is a similar answer to this problem here.
In order to make it work with your solution you have to make some changes to your code. First of all remove the new() from your generic class Property declaration otherwise you will always get an error from the compiler if you try to use a type T with no parameter-less constructor. Second add this method to your Property class:
private T GetInstance()
{
return (T)FormatterServices.GetUninitializedObject(typeof(T)); //does not call ctor
}
It will return an unitialized object but it won't call the constructor.
Here is the full code:
public class Property<T> where T : class
{
private T _value;
public T Value
{
get
{
return _value ?? (_value = GetInstance());
}
set
{
// insert desired logic here
_value = value;
}
}
private T GetInstance()
{
return (T)FormatterServices.GetUninitializedObject(typeof(T)); //does not call ctor
}
public static implicit operator T(Property<T> value)
{
return value.Value;
}
public static implicit operator Property<T>(T value)
{
return new Property<T> { Value = value };
}
}
I created a gist here, you can try the code into LinqPad and see the result.
hope it helps.
I've created a simple chatroom program .
Basic message sending and receiving works well on the client, and server,too.
But I want to broadcast the connection list to all users when any user leave.
I use a thread to handle this task.
private void monitorProc()
{
while(true)
{
try
{
int bytereads = 0;
byte[] buffer = new byte[4096];
bytereads = clientStream.Read(buffer, 0, buffer.Length);
updateMonitor(encoder.GetString(buffer), monitor);
}
catch
{
}
}
}
And I use this thread like this ...
monitorThread = new Thread(new ThreadStart(monitorProc));
monitorThread.Start();
And server is ...
private void broadcast(string message)
{
foreach (User user in userlist)
{
NetworkStream clientStream;
clientStream = user.Userclient.GetStream();
clientStream.Write(encoder.GetBytes(message), 0, 4096);
clientStream.Flush();
}
}
When server received message from client , it call this function , and then all user will
In that way , client will put the message when receive message from server.
I define a User class . It contain the following info...
[Serializable]
public class User
{
public enum stat
{
ALIVE,
DEAD
}
private string username;
private IPEndPoint userEP;
private TcpClient userclient;
private stat userstat;
public string Username { get; set; }
public IPEndPoint UserEP { get { return userEP; } }
public TcpClient Userclient { get { return userclient; } }
public stat Userstat { get; set; }
public User(TcpClient _userclient)
{
userclient = _userclient;
userstat = stat.ALIVE;
}
}
I use a list to store every user in server .
Question1: how to send the list to every client ?
(I know that serialization can help . But an error occurred when I use the following code to serialize the list...
foreach (User user in userlist) {
Stream statBroadcast = user.Userclient.GetStream();
var bin = new BinaryFormatter();
bin.Serialize(statBroadcast , userlist);
connlist.Items.Add(user.Username);
}
It said that I didn't mark TcpClient as serializable.
Question 2: If I can send custom object to client . How to send connection list and message at the same time ?
(If I define a function in client called "updateList" and it receive the data through stream . How to distinct from the data (whether message of connectionlist))