2012-03-21 3 views
1

비동기 소켓 서버를 처리하는 SocketSvr 클래스가 있습니다. 그것은 내 기본 폼에서 BackgroundWorker를 통해 호출됩니다. 기본적으로 메인 폼의 텍스트 상자에 소켓 데이터 정보를 표시하고 메인 폼에서 서버 시작/중지 버튼을 사용하여 해당 작업을 수행하기를 원합니다. 기본적 startserver는() 또는 중지 stopServer() 함수를 호출하여, 매우 간단 상기VB.Net 백그라운드 작업자에서 실행중인 비동기 소켓 서버를 올바르게 종료합니다.

Public Class Form1 

    Dim WithEvents Socketsvr As New SocketSvr 

    Private Sub ToggleServerButton_Click(ByVal sender As System.Object, _ 
     ByVal e As System.EventArgs) Handles ToggleServerButton.Click 

     If ToggleServerButton.Text = "Stop Server" Then 
      ToggleServerButton.Text = "Start Server" 
      Socketsvr.StopServer() 
     Else 
      ToggleServerButton.Text = "Stop Server" 
      Socketsvr.StartServer() 
     End If 
    End Sub 

    Private Sub UpdateOutput_Event(ByVal sender As Object, _ 
     ByVal text As String) Handles Socketsvr.UpdateOutput 
     Me.ServerOutputTextbox.AppendText(text + vbCrLf) 
    End Sub 

End Class 

: 여기

는 Form1.vb에서의 코드이다. 아래 이벤트는 백그라운드 프로세스에서 호출 된 이벤트를 통해 텍스트 상자를 업데이트하는 데 사용하는 Raised 이벤트입니다.

다음은 Socketsvr.vb의 코드 중 일부입니다. 게시물의 출혈을 최소화하기 위해 관련성없는 코드를 제거합니다.

Public Sub StopServer() 
    bw.CancelAsync() 
    allDone.Set() 
End Sub 'StopServer 

Public Sub StartServer() 
    bw.WorkerSupportsCancellation = True 
    bw.RunWorkerAsync() 
End Sub 'StartServer 

Private Sub bw_DoWork(ByVal sender As System.Object, _ 
    ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bw.DoWork 
    ' Data buffer for incoming data. 
    Dim bytes() As Byte = New [Byte](1023) {} 

    ' Establish the local endpoint for the socket. 
    Dim ipHostInfo As New IPHostEntry 
    ipHostInfo.AddressList = New IPAddress() _ 
     {New IPAddress(New [Byte]() {127, 0, 0, 1})} 
    Dim ipAddress As IPAddress = ipHostInfo.AddressList(0) 
    Dim localEndPoint As New IPEndPoint(ipAddress, 8888) 

    ' Create a TCP/IP socket. 
    Dim listener As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) 

    ' Bind the socket to the local endpoint and listen for incoming connections. 
    listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1) 
    listener.Bind(localEndPoint) 
    listener.Listen(100) 
    While True 
     ' If cancellation is pending, shut down server 
     If bw.CancellationPending Then 
      Out("Server stopped at " + DateTime.Now.ToString()) 
      Exit While 
     End If 
     Out("Server started at " + DateTime.Now.ToString()) 
     ' Set the event to nonsignaled state. 
     allDone.Reset() 

     Try 
      ' Start an asynchronous socket to listen for connections. 
      listener.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), listener) 
     Catch ex As Exception 
      MessageBox.Show(ex.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error) 
     End Try 

     ' Wait until a connection is made and processed before continuing. 
     allDone.WaitOne() 
    End While 
End Sub 

StopServer() 기능을 실행할 때 클라이언트의 요청을 올바르게 종료하는 방법을 잘 모릅니다. 위의 코드에서 백그라운드 프로세스의 취소 대기열을 배치합니다. 재미있는 점은 일단 StopServer()가 실행되면 더 많은 연결을 수락 한 다음 그 후에 수락을 중지한다는 것입니다.

나는 위의 코드에서 다음 줄을 제거하는 경우 :

listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1) 

- 나는 내 생각은 (나는 분명히 소켓을 다시 사용할 수 없습니다 불평) 서버를 두 번 시작하려고 할 때 충돌 배경 노동자에게 bw.CancellationPending 콜에 뭔가를 추가해야합니까?

더 자세한 정보가 필요하면 통찰력을 주시기 바랍니다.

답변

3

while 루프를 사용하기 때문에 MSDN의 예제 코드가 마음에 들지 않습니다. 필요하다고 생각하게 만듭니다. 필요한 경우 실제로 다른 스레드에서 수신해야합니다. 다행히도 BackgroundWorker을 사용할 필요가 없으며 UI 스레드를 잠그지 않고 while 루프 외부의 모든 작업을 쉽게 수행 할 수 있습니다.

먼저 Socket.BeginAccept을 수행하는 Accept(Socket)이라는 Shared 메서드 (다른 공유 콜백 및 메서드와 함께 사용)를 만듭니다. 또 다른 방법은 두 곳에서 실행되기 때문에 더 쉬우 며 나중에 예외를 잡을 필요가 있음을 알 수 있습니다. 주 Form의 다른 곳에서 IPEndPoint을 바인딩하고 청취 한 후에는 Accept에 첫 번째 전화를 연결하여 청취에 사용되는 소켓을 제공합니다.

또한 AcceptCallback에서 얻은 소켓이 새로 연결된 클라이언트 인 경우 Receive(Socket) 메서드를 만드는 것이 좋습니다. 또한 예외를 잡는 것과 같이 BeginReceive 이상의 코드가 더 많아지기 때문에 조직도 개선 될 것입니다.

AcceptCallback 방법에서 Receive을 수행 한 후에는 Accept으로 다시 전화 할 것입니다. 계속해서 AcceptAcceptCallback 사이에서 반복됩니다.청취를 중단이 루프를 중단하기 위해, 단지의 Socket.Close 메소드를 호출 Nothing으로 소켓 필드를 설정하고, 이러한 예외 잡기 : (닫습니다 정말 관련이있는)

  • SocketException
  • ObjectDisposedException (습니다 체크 단지 그 후, 소켓이 배치되고 null로 설정됩니다 통과 하겠지만
  • NullReferenceException이 01 년)

을 (A 무효 (아무것도 어디 가끔 경쟁 조건이 발생합니다)를 처분)입니다메서드를 사용하는 경우 Socket을 Nothing으로 설정해야합니다 (소켓을 닫은 후). 소켓이 삭제되었음을 알립니다. 또한 리스너가 아무런 메소드도 실행하지 않고 있는지 확인해야합니다 (이 경우 AcceptAcceptCallback 메소드). 가끔 경쟁 조건으로 NullReferenceExceptions을 catch해야합니다.

다음은 Accept 메소드의 기본 모델입니다. 이 코드는 나머지 코드와 비슷한 모델입니다. 이것에 대한

Private Shared Sub AcceptCallback(ar As IAsyncResult) 
    Dim listener As Socket = DirectCast(ar.AsyncState, Socket) 

    If listener Is Nothing Then Return 

    Try 
     Dim newClient As Socket = listener.EndAccept(ar) 

     Receive(newClient) 
    Catch ex1 As SocketException 
     Return 
    Catch ex2 As ObjectDisposedException 
     Return 
    Catch ex3 As NullReferenceException 
     Return 
    End Try 

    Accept(listener) 
End Sub 
+0

감사합니다 - 아직도 내가 VB.Net 매우 미숙 한 생각으로 주위에 내 머리를 정리하려고은 - 곧 다시 연락하고 그것을 모두 밖으로 어떻게 작동하는지 볼 수 있습니다. 잠시 동안 +1을하고 일단 내가 일하면 답을 표시 할 것입니다. –

+0

업데이트 : 이것은 매력처럼 일했습니다. 정말 고마워요! –

+0

나는 또한 비동기 서버 샘플을 기반으로 소켓 서버에 대해 wpf UI와 동일한 작업을 수행하고 싶습니다. 나는 그것을 누군가가 C#에서 수정 된 코드를 제공 할 수 주셔서 감사하겠습니다. –

관련 문제