면책 조항 : 내 C#을 내 C++ 난 내의 구성 요소에 대한 테스트 응용 프로그램을 작성하기 위해 C#으로 비동기 소켓 작업을 수행하는 방법을 배우려고 노력하고인터 로킹 대기 == 0?
만큼 좋은 꿈에도 생각하지 않습니다. 하여 TcpClient를 사용하여 내 이전의 시도는 실패로 끝났다 당신이 여기에에 뛰어난 질문을 읽을 수 있습니다 : 그 작업을 얻을 수 없었다
TcpClient.NetworkStream Async operations - Canceling/Disconnect
Detect errors with NetworkStream.WriteAsync
, 이후, 나는 Socket.BeginX 및 소켓을 사용하여 시도 .EndX 대신. 나는 훨씬 더 나아 갔다. 내 문제는 지금은 다시 종료하고 소켓에 가까운 호출하는, 분리 시간을 때 아래 목록에, 비동기 작업이 여전히 뛰어난 있다는 것입니다 그들은 개체 배치 예외가 발생합니다 또는 객체가 null의 예외로 설정됩니다.
내가 여기에 대한 유사한 게시물을 발견 :After disposing async socket (.Net) callbacks still get called
그러나, 나는 그 대답을 허용하지 않습니다, 당신은 에 대한 예외를 사용하는 경우 것이 동작을 목적으로하기 때문에, 다음 1) 그들은 뛰어난 없습니다 2) 당신은 예외가 의도 한 경우에 발생 된 경우 말할 수 또는 당신이 실제로 소켓 이외의 배치 오브젝트, 또는 비동기 방식에서 null 참조를 사용하기 때문에가 발생 된 경우.
비동기 소켓 코드가있는 C++에서 인터록이있는 비동기 작업의 수를 추적하고 연결을 끊을 시간이 왔을 때 종료를 호출 한 다음 인터록이 0이 될 때까지 기다린 다음 내가 필요한 모든 회원을 닫고 파괴하라.
나는 다음과 같은 목록에 C#으로 완료되지 않은 모든 비동기 작업에 내 분리 방법에서 대기 가겠어요 어떻게? 홈페이지
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using System.Net.Sockets;
using System.Net;
namespace IntegrationTests
{
public class Client2
{
class ReceiveContext
{
public Socket m_socket;
public const int m_bufferSize = 1024;
public byte[] m_buffer = new byte[m_bufferSize];
}
private static readonly ILog log = LogManager.GetLogger("root");
static private ulong m_lastId = 1;
private ulong m_id;
private string m_host;
private uint m_port;
private uint m_timeoutMilliseconds;
private string m_clientId;
private Socket m_socket;
private uint m_numOutstandingAsyncOps;
public Client2(string host, uint port, string clientId, uint timeoutMilliseconds)
{
m_id = m_lastId++;
m_host = host;
m_port = port;
m_clientId = clientId;
m_timeoutMilliseconds = timeoutMilliseconds;
m_socket = null;
m_numOutstandingAsyncOps = 0;
}
~Client2()
{
Disconnect();
}
public void Connect()
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(m_host);
IPAddress[] ipV4Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray();
IPAddress[] ipV6Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
IPEndPoint endpoint = new IPEndPoint(ipV4Addresses[0], (int)m_port);
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_socket.ReceiveTimeout = (int)m_timeoutMilliseconds;
m_socket.SendTimeout = (int)m_timeoutMilliseconds;
try
{
m_socket.Connect(endpoint);
log.Info(string.Format("Connected to: {0}", m_socket.RemoteEndPoint.ToString()));
// Issue the next async receive
ReceiveContext context = new ReceiveContext();
context.m_socket = m_socket;
m_socket.BeginReceive(context.m_buffer, 0, ReceiveContext.m_bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), context);
}
catch (Exception e)
{
// Error
log.Error(string.Format("Client #{0} Exception caught OnConnect. Exception: {1}"
, m_id, e.ToString()));
}
}
public void Disconnect()
{
if (m_socket != null)
{
m_socket.Shutdown(SocketShutdown.Both);
// TODO - <--- Error here in the callbacks where they try to use the socket and it is disposed
// We need to wait here until all outstanding async operations complete
// Should we use Interlocked to keep track of them and wait on it somehow?
m_socket.Close();
m_socket = null;
}
}
public void Login()
{
string loginRequest = string.Format("loginstuff{0})", m_clientId);
var data = Encoding.ASCII.GetBytes(loginRequest);
m_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), m_socket);
}
public void MakeRequest(string thingy)
{
string message = string.Format("requeststuff{0}", thingy);
var data = Encoding.ASCII.GetBytes(message);
m_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), m_socket);
}
void OnReceive(IAsyncResult asyncResult)
{
ReceiveContext context = (ReceiveContext)asyncResult.AsyncState;
string data = null;
try
{
int bytesReceived = context.m_socket.EndReceive(asyncResult);
data = Encoding.ASCII.GetString(context.m_buffer, 0, bytesReceived);
ReceiveContext newContext = new ReceiveContext();
newContext.m_socket = context.m_socket;
m_socket.BeginReceive(newContext.m_buffer, 0, ReceiveContext.m_bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), newContext);
}
catch(SocketException e)
{
if(e.SocketErrorCode == SocketError.ConnectionAborted) // Check if we disconnected on our end
{
return;
}
}
catch (Exception e)
{
// Error
log.Error(string.Format("Client #{0} Exception caught OnReceive. Exception: {1}"
, m_id, e.ToString()));
}
}
void OnSend(IAsyncResult asyncResult)
{
Socket socket = (Socket)asyncResult.AsyncState;
try
{
int bytesSent = socket.EndSend(asyncResult);
}
catch(Exception e)
{
log.Error(string.Format("Client #{0} Exception caught OnSend. Exception: {1}"
, m_id, e.ToString()));
}
}
}
}
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using log4net.Config;
namespace IntegrationTests
{
class Program
{
private static readonly ILog log = LogManager.GetLogger("root");
static void Main(string[] args)
{
try
{
XmlConfigurator.Configure();
log.Info("Starting Component Integration Tests...");
Client2 client = new Client2("127.0.0.1", 24001, "MyClientId", 60000);
client.Connect();
client.Login();
client.MakeRequest("StuffAndPuff");
System.Threading.Thread.Sleep(60000); // Sim work until user shutsdown
client.Disconnect();
}
catch (Exception e)
{
log.Error(string.Format("Caught an exception in main. Exception: {0}"
, e.ToString()));
}
}
}
}
편집 :
여기 내 최선을 다해 EVK에 의해 제안 된 대답을 사용하여 내 추가 시도가있다. 내가 말할 수있는 한 잘 작동합니다. 이와
문제는 내가 기본적으로 인해 카운터 또는 소켓의 상태를 변경 할 수있는 모든 주위에 고정 할 수있는 요구 사항, 동기 호출에 비동기 있었다 모든 것을 만들어 같은 느낌이다. 다시 말하지만, 저는 C++에 비해 C#에 초보자입니다. 그래서 그의 대답을 해석하는 표를 완전히 놓친 경우를 지적하십시오.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IntegrationTests
{
public class Client
{
class ReceiveContext
{
public const int _bufferSize = 1024;
public byte[] _buffer = new byte[_bufferSize]; // Contains bytes from one receive
public StringBuilder _stringBuilder = new StringBuilder(); // Contains bytes for multiple receives in order to build message up to delim
}
private static readonly ILog _log = LogManager.GetLogger("root");
static private ulong _lastId = 1;
private ulong _id;
protected string _host;
protected int _port;
protected int _timeoutMilliseconds;
protected string _sessionId;
protected Socket _socket;
protected object _lockNumOutstandingAsyncOps;
protected int _numOutstandingAsyncOps;
private bool _disposed = false;
public Client(string host, int port, string sessionId, int timeoutMilliseconds)
{
_id = _lastId++;
_host = host;
_port = port;
_sessionId = sessionId;
_timeoutMilliseconds = timeoutMilliseconds;
_socket = null;
_numOutstandingAsyncOps = 0;
_lockNumOutstandingAsyncOps = new object();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if(_disposed)
{
return;
}
if (disposing)
{
_socket.Close();
}
_disposed = true;
}
public void Connect()
{
lock (_lockNumOutstandingAsyncOps)
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(_host);
IPAddress[] ipV4Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray();
IPAddress[] ipV6Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
IPEndPoint endpoint = new IPEndPoint(ipV4Addresses[0], _port);
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.ReceiveTimeout = _timeoutMilliseconds;
_socket.SendTimeout = _timeoutMilliseconds;
try
{
_socket.Connect(endpoint);
}
catch (Exception e)
{
// Error
Debug.WriteLine(string.Format("Client #{0} Exception caught OnConnect. Exception: {1}"
, _id, e.ToString()));
return;
}
Debug.WriteLine(string.Format("Client #{0} connected to: {1}", _id, _socket.RemoteEndPoint.ToString()));
// Issue the first async receive
ReceiveContext context = new ReceiveContext();
++_numOutstandingAsyncOps;
_socket.BeginReceive(context._buffer, 0, ReceiveContext._bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), context);
}
}
public void Disconnect()
{
if (_socket != null)
{
// We need to wait here until all outstanding async operations complete
// In order to avoid getting 'Object was disposed' exceptions in those async ops that use the socket
lock(_lockNumOutstandingAsyncOps)
{
Debug.WriteLine(string.Format("Client #{0} Disconnecting...", _id));
_socket.Shutdown(SocketShutdown.Both);
while (_numOutstandingAsyncOps > 0)
{
Monitor.Wait(_lockNumOutstandingAsyncOps);
}
_socket.Close();
_socket = null;
}
}
}
public void Login()
{
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
string loginRequest = string.Format("loginstuff{0}", _clientId);
var data = Encoding.ASCII.GetBytes(loginRequest);
Debug.WriteLine(string.Format("Client #{0} Sending Login Request: {1}"
, _id, loginRequest));
++_numOutstandingAsyncOps;
_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), _socket);
}
else
{
Debug.WriteLine(string.Format("Client #{0} Login was called, but Socket is null or no longer connected."
, _id));
}
}
}
public void MakeRequest(string thingy)
{
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
string message = string.Format("requeststuff{0}", thingy);
var data = Encoding.ASCII.GetBytes(message);
Debug.WriteLine(string.Format("Client #{0} Sending Request: {1}"
, _id, message));
++_numOutstandingAsyncOps;
_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), _socket);
}
else
{
Debug.WriteLine(string.Format("Client #{0} MakeRequest was called, but Socket is null or no longer connected."
, _id));
}
}
}
protected void OnReceive(IAsyncResult asyncResult)
{
lock (_lockNumOutstandingAsyncOps)
{
ReceiveContext context = (ReceiveContext)asyncResult.AsyncState;
string data = null;
try
{
int bytesReceived = _socket.EndReceive(asyncResult);
data = Encoding.ASCII.GetString(context._buffer, 0, bytesReceived);
// If the remote host shuts down the Socket connection with the Shutdown method, and all available data has been received,
// the EndReceive method will complete immediately and return zero bytes
if (bytesReceived > 0)
{
StringBuilder stringBuilder = context._stringBuilder.Append(data);
int index = -1;
do
{
index = stringBuilder.ToString().IndexOf("#");
if (index != -1)
{
string message = stringBuilder.ToString().Substring(0, index + 1);
stringBuilder.Remove(0, index + 1);
Debug.WriteLine(string.Format("Client #{0} Received Data: {1}"
, _id, message));
}
} while (index != -1);
}
}
catch (SocketException e)
{
// Check if we disconnected on our end
if (e.SocketErrorCode == SocketError.ConnectionAborted)
{
// Ignore
}
else
{
// Error
Debug.WriteLine(string.Format("Client #{0} SocketException caught OnReceive. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
}
catch (Exception e)
{
// Error
Debug.WriteLine(string.Format("Client #{0} Exception caught OnReceive. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
finally
{
--_numOutstandingAsyncOps;
Monitor.Pulse(_lockNumOutstandingAsyncOps);
}
}
// Issue the next async receive
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
++_numOutstandingAsyncOps;
ReceiveContext newContext = new ReceiveContext();
_socket.BeginReceive(newContext._buffer, 0, ReceiveContext._bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), newContext);
}
}
}
protected void OnSend(IAsyncResult asyncResult)
{
lock (_lockNumOutstandingAsyncOps)
{
try
{
int bytesSent = _socket.EndSend(asyncResult);
}
catch (Exception e)
{
Debug.WriteLine(string.Format("Client #{0} Exception caught OnSend. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
finally
{
--_numOutstandingAsyncOps;
Monitor.Pulse(_lockNumOutstandingAsyncOps);
}
}
}
}
}
참고. 당신은 당신의 객체에 파이널 라이저를 전혀 가지고 있지 않아야합니다. – Servy
는 ['CountdownEvent'] (https://docs.microsoft.com/dotnet/api/system.threading.countdownevent) 당신의 친구입니다. 당신이 C++ 배경에서있는 한 –
관리 파이 나라와 C++ 소멸자를 혼동하지 마십시오, Servy는 말에 추가합니다.일부 문서에서 C#이 구문을 빌리고 심지어 "destructor"라는 단어를 사용한다는 사실은 실제로 매우 불행합니다. 'IDisposable'는 C#이 결정 론적 정리에 사용하는 것이며 finalizer는 거의 필요하지 않습니다. 개체가 가비지 수집 될 때 관리되지 않는 리소스를 릴리스하기위한 최종 단초가됩니다. 일반적으로 이러한 개체는 이미 호출 된 Dispose를 호출해야합니다. finalizer가 표시되지 않음). –