Silverlight 4에서 소켓을 사용하여 데이터를주고받는 클래스가 있습니다. 일부 인터페이스는 기존 인터페이스를 구현해야하므로 다소 이상하게 보일 수 있지만 는 여기있다 :SL4 AsyncEventArgs가 두 번째 소켓 연결 후 InvalidOperationException을 throw합니다.
public class TcpDataTransportClient : IDataTransportService
{
private const string TCP_ADDRESS_SETTING = "tcpaddress";
private const string TCP_PORT_SETTING = "tcpport";
private static ManualResetEvent clientConnected = new ManualResetEvent(false);
private static ManualResetEvent clientDataReceived = new ManualResetEvent(false);
private static ManualResetEvent clientDataSent = new ManualResetEvent(false);
private Dictionary<string, object> settings = new Dictionary<string, object>();
private IDataEncapsulator dataEncapsulator;
private IDataCollector dataCollector;
private Socket client;
private SocketAsyncEventArgs clientArgs;
public event DataReceivedHandler OnDataReceived;
public event DataSentHandler OnDataSent;
public TcpDataTransportClient()
{
}
public Dictionary<string, object> Settings
{
get
{
return this.settings;
}
set
{
this.settings = value;
}
}
public IDataEncapsulator DataEncapsulator
{
get
{
return this.dataEncapsulator;
}
set
{
this.dataEncapsulator = value;
}
}
public void Start(IDataCollector dataCollector)
{
this.dataCollector = dataCollector;
clientArgs = new SocketAsyncEventArgs();
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientArgs.Completed += clientArgs_Completed;
clientArgs.UserToken = client;
clientArgs.RemoteEndPoint = GetIPEndPoint();
client.ConnectAsync(clientArgs);
clientConnected.WaitOne();
}
private IPEndPoint GetIPEndPoint()
{
IPAddress ipAddress;
int tcpPort;
if (!IPAddress.TryParse(settings[TCP_ADDRESS_SETTING].ToString(), out ipAddress))
throw new ArgumentException(String.Format("Invalid setting for IP Address: '{0}'", TCP_ADDRESS_SETTING));
if (!int.TryParse(settings[TCP_PORT_SETTING].ToString(), out tcpPort))
throw new ArgumentException(String.Format("Invalid setting for TCP Port: '{0}'", TCP_PORT_SETTING));
return new IPEndPoint(ipAddress, tcpPort);
}
void clientArgs_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
ProcessConnect(e);
break;
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new Exception("Invalid operation completed");
}
}
private void ProcessConnect(SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success)
{
throw new SocketException((int)e.SocketError);
}
else
{
clientConnected.Set();
}
}
private void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
var socket = e.UserToken as Socket;
var response = dataCollector.Collect(e.Buffer);
if (response != null)
{
if (this.OnDataReceived != null)
this.OnDataReceived(response);
clientDataReceived.Set();
}
else
{
bool willRaiseEvent = socket.ReceiveAsync(clientArgs);
if (!willRaiseEvent)
ProcessReceive(e);
}
}
else
{
throw new SocketException((int)e.SocketError);
}
}
private void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
var socket = e.UserToken as Socket;
if (OnDataSent != null)
OnDataSent(clientArgs.Buffer);
clientDataSent.Set();
clientDataReceived.Reset();
bool willRaiseEvent = socket.ReceiveAsync(e);
if (!willRaiseEvent)
ProcessReceive(e);
clientDataReceived.WaitOne();
}
else
{
throw new SocketException((int)e.SocketError);
}
}
public void Stop()
{
client.Shutdown(SocketShutdown.Send);
client.Close();
client.Dispose();
clientArgs.Dispose();
}
public void Write(byte[] data)
{
clientDataSent.Reset();
clientArgs.SetBuffer(data, 0, data.Length);
bool willRaiseEvent = client.SendAsync(clientArgs);
if (!willRaiseEvent)
ProcessSend(clientArgs);
clientDataSent.WaitOne();
}
}
여기 아이디어는 모든 요청 (데이터를 전송)는 항상 응답 (데이터를 수신) 응답되어, 당신은 분리하고 새로운 연결을 생성하지 않는 한 그것만큼 잘 작동 . 예를 들어
:
client.Connect();
client.ClearConfiguration(1);
var status = client.RequestStatusDetails(1);
client.Disconnect();
이 코드는 여러 개의 요청을 전송하고 각에 대한 답변을받습니다. 당신이 (또는 루프) 다시 동일한 코드를 실행하는 경우 그러나 연결이 확립되어 있지만 곧 코드와이 지점에 도달 :
public void Write(byte[] data)
{
clientDataSent.Reset();
clientArgs.SetBuffer(data, 0, data.Length);
bool willRaiseEvent = client.SendAsync(clientArgs);
if (!willRaiseEvent)
ProcessSend(clientArgs);
clientDataSent.WaitOne();
}
예외가 client.SendAsync에 대해 발생합니다
(clientArgs는); 그냥이 문 앞에 중단 점을 넣어하지만, 는 VS2010 휴식을 할 경우,이 SocketAsyncEventArgs 예를
를 사용하여 이미 진행
비동기 소켓 작업이 :
이
는 예외 그것에, 다음 디버깅 계속 작동합니다.나는이 문제의 원인을 정확히 알 수 없으므로, 과 추가 정보가 없습니다.
제안 사항?
거기에 와일드 록이 있습니다. 코드가 동시에 여러 요청을 보내는 것을 허용하지 않습니다. 각각의 비동기 요청에 대해이를 거부하거나 새로운'SocketAsyncEventArgs' 인스턴스를 생성하십시오. –
오 ... 정말 여러 요청을 거부해야합니다, 내가 뭘 잘못하고 있는지 나타낼 수 있습니까? – TimothyP
@TimothyP :'client.SendAsync'를 호출하기 전에'clientDataSent.WaitOne'을 사용해야합니다. 작업이 완료되면'clientDataSent.Set()'을 사용하십시오. 또한,'ManualResetEvent' 대신에'AutoResetEvent'를 사용하고, 작업이 전혀 완료되지 않을 경우 기다리는 타임 아웃을 사용하는 것을 고려하십시오. –