В моем клиентском приложении каждый клиент получает два Socket
, один для получения и другой для отправки и подключения к серверу следующим образом:
async void Connect2Server(object obj)
{
recvArgs = new SocketAsyncEventArgs();
sendArgs = new SocketAsyncEventArgs();
recvArgs.AcceptSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sendArgs.AcceptSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await recvArgs.AcceptSocket.ConnectAsync(host, port);
await recvArgs.AcceptSocket.SendAsync(Info<SockInfo>.Pack(new SockInfo()
{
Name = ClientName,
RecvArg = true
}), SocketFlags.None);
await sendArgs.AcceptSocket.ConnectAsync(host, port);
await sendArgs.AcceptSocket.SendAsync(Info<SockInfo>.Pack(new SockInfo()
{
Name = ClientName,
RecvArg = false
}), SocketFlags.None);
SetBuffer(recvArgs, bufferSize);
recvArgs.Completed += Receive;
recvArgs.AcceptSocket.ReceiveAsync(recvArgs);
}
и в AcceptAsync
обратном вызове сервера. Ясделав это:
void Accepted(object sender, SocketAsyncEventArgs e)
{
e.Completed -= Accepted;
e.Completed += CheckInfo;
SetBuffer(e, infoBufferLength);
e.AcceptSocket.ReceiveAsync(e);
Accept();
}
серверная CheckInfo
функция проверяет SockInfo
, отправленный клиентом. Если RecvArg = true
сервер назначает SocketAsyncEventArgs
в качестве SendArgs
, в противном случае RecvArgs
, например, так:
void CheckInfo(object sender, SocketAsyncEventArgs e)
{
lock (Clients)
{
e.Completed -= CheckInfo;
var info = Info<SockInfo>.Unpack(e.Buffer);
Client c = Clients.FirstOrDefault(x => x.Name == info.Name);
if (c == null)
{
c = new Client();
c.Name = info.Name;
Clients.Add(c);
}
if (info.RecvArg)
{
c.SendArgs = e;
c.SendArgs.SetBuffer(null);
c.SendArgs.Completed += Sent;
}
else
{
c.RecvArgs = e;
SetBuffer(c.RecvArgs, bufferLength);
c.RecvArgs.Completed += Received;
c.RecvArgs.AcceptSocket.ReceiveAsync(c.RecvArgs);
}
}
}
Clients
является ObservableCollection
из Client
:
class Client
{
public string Name { get; set; }
public SocketAsyncEventArgs SendArgs { get; set; }
public SocketAsyncEventArgs RecvArgs { get; set; }
}
и SockInfo
- это структура со следующими свойствами:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SockInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string Name;
public bool RecvArg;
}
Если я установлю 3 точки останова: первую и вторую в Connect2Server
функции клиентского приложения, 2-ю и 4-ю await
и третью в сервереCheckInfo
и запустите оба приложения в режиме отладки, CheckInfo
дважды ударит, и обе функции c.SendArgs
и c.RecvArgs
в CheckInfo
получат SocketAsyncEventArgs
. Однако если я удаляю первые две точки останова, CheckInfo
поражается только один раз почти всегда, а c.RecvArgs
остается null
!
Почему и как я могу убедиться, что c.RecvArgs
всегда получает свое SocketAsyncEventArgs
?
Другой вопрос: нужно ли иметь lock
на Clients
в CheckInfo
?
EDIT
Twoдля этого вопроса могут иметь отношение другие функции (1) StartServer
, где сервер начинает прослушивание и передает управление (2) Accept
, что создает новый SocketAsyncEventArgs
при каждом вызове. Вот эти два:
void StartServer(object obj)
{
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(0, port));
server.Listen(0);
Accept();
}
void Accept()
{
var arg = new SocketAsyncEventArgs();
arg.Completed += Accepted;
try { server.AcceptAsync(arg); }
catch (Exception)
{
arg.Dispose();
StopServer(null);
}
}