[vb.net 2005] sockets: data uitlezen van 1 bepaald IP adres

Pagina: 1
Acties:
  • 672 views sinds 30-01-2008
  • Reageer

Acties:
  • 0 Henk 'm!

  • mmy841
  • Registratie: Juli 2004
  • Laatst online: 09-07-2021
Een server stuurt data uit op een bepaalde poort. Het is de bedoeling dat ik met mijn pc gewoon naar het IP adres van deze server luister, op die poort. Voorlopig is het dus niet nodig dat ik zelf data stuur.

In het verleden maakte ik gebruik van vb6 en de winsock control. Nu wil ik dus omschakelen naar vb.net en sockets gebruiken. Na wat rondgezocht te hebben ben ik er al iets wijzer uit geraakt. Met onderstaand voorbeeld kan ik al de gewenste data uitlezen (dit ook ter info voor degenen die op zoek zijn naar nog een voorbeeld over sockets).

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Imports System.Net
Imports System.Net.Sockets

Public Class Form1

Private ClientSocket As Socket
 'om bytes om te zetten naar een string & omgekeerd (voor versturen & ontvangen)
Private ASCII As New System.Text.ASCIIEncoding()                   

Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click

    'naam van de server omzetten naar IP adres
    Dim Addr As IPAddress
    Addr = Dns.GetHostEntry(txtAddress.Text).AddressList(0)

    'IP Endpoint maken (= de server waarmee we willen connecteren)
    Dim EP As New IPEndPoint(Addr, CInt(txtPort.Text))

    'socket aanmaken
    ClientSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

    'connectie maken -> dit gaan we asynchroon (callback) doen (in de functie ConnectCallback)
    ClientSocket.BeginConnect(EP, AddressOf ConnectCallback, Nothing)                           

End Sub

Private Sub ConnectCallback(ByVal ar As IAsyncResult)
    ClientSocket.EndConnect(ar)
    Dim bytes(4095) As Byte
    ClientSocket.BeginReceive(bytes, 0, bytes.Length, SocketFlags.None, AddressOf ReceiveCallback, bytes)
end sub

Private Sub ReceiveCallback(ByVal ar As IAsyncResult)
    'deze functie wordt uitgevoerd op de achtergrond als er data ontvangen wordt

    'hierin gaan we de ontvangen data opslaan
    Dim bytes() As Byte                                             
    bytes = CType(ar.AsyncState, Byte())

    'onthouden i/e variabele hoeveel bytes ontvangen zijn
    Dim numbytes As Int32 = ClientSocket.EndReceive(ar)

    If numbytes = 0 Then                                         'indien aantal ontvangen bytes = 0
       ClientSocket.Shutdown(SocketShutdown.Both) 'connectie sluiten
       ClientSocket.Close()
       'DisConnectedUI is de sub waarmee we de info op het main form aanpassen
       Dim dlg As New NoParamsDelegate(AddressOf DisconnectedUI)   
       Me.Invoke(dlg)                                              
    Else                                                                 'meer dan 0 bytes ontvangen...
       Dim Recv As String                                        'variabele om de data in te plaatsen
       Recv = ASCII.GetString(bytes, 0, numbytes)    'in bytes staat de ontvangen data -> dit omzetten naar een string
       Array.Clear(bytes, 0, bytes.Length)
       'tonen in main form
       Dim dlg As New OneStringDelegate(AddressOf DisplayReceivedData) 
       Dim args() As Object = {Recv}
       Me.Invoke(dlg, args)

       'data terug ontvangen
       ClientSocket.BeginReceive(bytes, 0, bytes.Length, SocketFlags.None, AddressOf ReceiveCallback, bytes)

    End If
End Sub

Private Sub DisplayReceivedData(ByVal data As String)
    'deze sub wordt uitgevoerd als er data ontvangen is (deze sub wordt geactiveerd via 
    txtOntvangen.Text = data
End Sub

Private Delegate Sub OneStringDelegate(ByVal data As String)
Private Delegate Sub NoParamsDelegate()

End Class


Nu heb ik nog enkele vragen...
- in verschillende fora en op het net lees ik dat er een verschil is tussen een TCPListener en sockets
- is er een mogelijkheid om met bovenstaand voorbeeld te controleren of de netwerkconnectie verbroken is, ... Op dit moment krijg ik een error in de ReceiveCall back functie (an existing connection was forcibly closed by the remote host) (of a socket operation was attempted to an unreachable host).
- indien ik connectie maak naar de mail server krijg ik onmiddellijk connectie. Indien ik connectie maak naar bv. de telefooncentrale hier die info uitstuurt krijg ik pas na een 20 tal seconden connectie (terwijl dit met winsock en vb6 ook vrijwel onmiddellijk ging).

Met een ander voorbeeld werd gebruik gemaakt van een NetworkStream en GetStream. Ook werd hier geen gebruik gemaakt van callback procedures, maar van een (bijna-)oneindige lus met Application.DoEvents. Met dit voorbeeld werd er echter gewoon geluisterd op mijn pc en gekozen poort of er data werd ontvangen of niet. Dus de server moest dan specifiek data uitsturen naar mijn IP adres...

Ik hoop hier wat meer info te ontvangen zodat ik terug in de juiste richting kan verderwerken...

Acties:
  • 0 Henk 'm!

  • mmy841
  • Registratie: Juli 2004
  • Laatst online: 09-07-2021
update...

Ik heb ondertussen een 2de methode uitgetest en deze connecteerd wel onmiddellijk met de telefooncentrale.

Hier wordt wel gebruik gemaakt van een TCPClient

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Imports System.Net.Sockets
Imports System.Text

Public Class Form1

Private ASCII As New System.Text.ASCIIEncoding
Private MyClient As TcpClient
Private mydata(4095) As Byte

Private Sub btnMaakConnectie_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnMaakConnectie.Click
    Try
       'connectie opzetten indien IP & poort beschikbaar zijn
       MyClient = New TcpClient(txtIP.Text, txtPoort.Text)
       'data uitlezen (asynchroon)
       MyClient.GetStream.BeginRead(mydata, 0, 4095, AddressOf DataUitlezen, Nothing)
    Catch ex As Exception
        ....    
    End Try
End Sub

Private Sub DataUitlezen(ByVal ar As IAsyncResult)
    'asynchrone callback functie om data uit te lezen
    Dim ingelezen_data_lengte As Integer                        
    Try
       ingelezen_data_lengte = MyClient.GetStream.EndRead(ar)  
       If ingelezen_data_lengte < 1 Then                       
           txtInfo.AppendText("Disconnected" & vbCrLf)
           Exit Sub
       End If

       'data in bytes omzetten naar een string
       Dim tekst As String
       tekst = ASCII.GetString(mydata, 0, ingelezen_data_lengte)
       '....
       'data nogmaals uitlezen...
       MyClient.GetStream.BeginRead(mydata, 0, 4095, AddressOf DataUitlezen, Nothing)

    Catch ex As Exception                                    
       MessageBox.Show("er kan geen data meer uitgelezen worden - fout = " & vbCrLf & ex.Message)
    End Try
End Sub

Private Sub btnConnectieVerbreken_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnectieVerbreken.Click
    Try
      MyClient.Close()
      MyClient = Nothing
    Catch ex As Exception
      Exit Sub
    End Try
End Sub
End Class


1) mbv try - catch controleer ik nu of de connectie gemaakt kan worden, of deze onderbroken is, ...
Zijn hier betere manieren voor?
2) als ik op btnConnectieVerbreken klik dan zou ik de verbinding volledig willen verbreken. Dit werkt wel, maar ik krijg wel elke keer een waarschuwing "A first chance exception of type 'System.NullReferenceException' occurred in ....' Vlak daarna krijg ik nogmaals de error uit het catch-gedeelte van de sub DataUitlezen.

Voor de rest lijkt deze code wel beter te werken. Enkel indien er geen netwerkconnectie beschikbaar is, of wanneer de hostname / IP onbekend is bevriest de user interface. Ook al zou de connectie asynchroon moeten gebeuren. Volgens mij is dit dan toch een soort aparte thread, waardoor er genoeg systeembronnen beschikbaar blijven voor de rest van de software?

Acties:
  • 0 Henk 'm!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 08-09 11:33
je maakt iig nergens een thread aan, dus zolang je geen verbinding kunt maken, wacht, of "stacked" je interface commando's.

Hoewel het lezen misschien a-synschroon gaat kun je dat beter helemaal in een apparte thread zetten om je UI responsive te houden :)

~ Mijn prog blog!


Acties:
  • 0 Henk 'm!

  • mmy841
  • Registratie: Juli 2004
  • Laatst online: 09-07-2021
met MSDN erlangs heb ik de werkwijze voor asynchrone connecties nog eens bekeken...

Ik maak nu gebruik van een Socket met de methodes BeginConnect / EndConnect, BeginSend / EndSend, BeginReceive / EndReceive.

Elke methode Begin.... die verwijst naar een Callback functie met de End... methode in.

Dit lijkt wel veel vlotter te gaan, maar heb hier jammer genoeg niet al te veel mee kunnen testen.

Maar als ik TCPClient en TCPListener wil gaan gebruiken, dan moet ik eigenlijk een aparte thread aanmaken? Of gewoon niet gebruiken omdat deze methoden synchroon zouden werken?