|
-
Mar 24th, 2008, 06:51 AM
#1
Thread Starter
Fanatic Member
[2008] TCP Sockets
Yes, i know, another TCP Sockets thread. There are many around, i've been reading them!
Athiest has been a big help here, reading up on his code, but my head just doesn't want to move from the VB6 mindset of winsock.
Any way, i'm attempting to create a P2P chat program. It's both a client and a server chat application in one. Let me describe it a bit:
There are a list of IP addresses (And ports, and alias names) that are kept, which is where all your messages are sent to (I know the IP Address and Port are the only bits needed).
If someone connects to you, then they are added to your list, so that any message you send is delivered to them (And anyone else on your list). Also you are added to their list, so that any message they send is delivered to you (and everyone else on their list).
This is the same if you connect to someone.
So in both instances of the program, there's a client and a server.
Now the bit that I'm having a hard time understanding is how to organize connected client sockets this way. Well just how these sockets work in general is still a bit confusing.
I'm confused about how to setup clients on a server, that have some type of ID (which in VB6 i would have used the index in an array of winsocks for and then used an array to keep the index binded to an IP, port, alias and anything else i wanted).
It also didn't matter who initiated the connection, because the listening winsock would just pass off the incoming connection (RequestID) to a newly created winsock for that client.
I'm having a hard time trying to think how this would be designed to achieve the same result in VB.NET.
Here is some of my code (That's been taken from Athiest any way):
Code:
Dim ListenThread As Threading.Thread
Dim Listener As Net.Sockets.TcpListener
Dim Clients As New List(Of ConnectedClient)
Dim readBuffer(255) As Byte
Private Client As Net.Sockets.TcpClient
Private ListenPort As Long
Private Sub Deactivate_Listening_Winsock()
'I Hope this is how it's done
Listener.Stop()
ListenThread.Abort()
End Sub
Private Sub Initiate_Listening_Winsock()
Listener = New Net.Sockets.TcpListener(Net.IPAddress.Any, ListenPort)
Listener.Start()
ListenThread = New Threading.Thread(AddressOf DoListen)
ListenThread.IsBackground = True
ListenThread.Start()
End Sub
Private Sub DoListen()
Dim incomingClient As System.Net.Sockets.TcpClient
Do
incomingClient = Listener.AcceptTcpClient 'Accept the incoming connection. This is a blocking method so execution will halt here until someone tries to connect.
Dim connClient As New ConnectedClient(incomingClient, Me) 'Create a new instance of ConnectedClient (check its constructor to see whats happening now).
AddHandler connClient.dataReceived, AddressOf Me.messageReceived
Clients.Add(connClient) 'Adds the connected client to the list of connected clients.
Loop
End Sub
Private Sub DoRead(ByVal ar As IAsyncResult)
Try
'EndRead returns the number of bytes that were recieved, so we will need to check so that something was recieved
Dim TotalBytes As Integer = Client.GetStream.EndRead(ar)
If TotalBytes > 0 Then
Dim strMessage As String = System.Text.Encoding.ASCII.GetString(readBuffer, 0, TotalBytes)
Data_Reciever(strMessage, Client.GetStream) 'This was an attempt here to try to
' Findout where this message came from
'Calling this method again is important, it begins the read again.
Client.GetStream.BeginRead(readBuffer, 0, 255, AddressOf DoRead, Nothing)
End If
Catch e As Exception
MessageBox.Show(e.Message)
End Try
End Sub
Private Sub Data_Reciever(ByVal Data_ As String, ByVal Client_ As Net.Sockets.NetworkStream)
'This is the sub that is going to be used to add new clients to the little database
'Also going to be used to parse the message data, or remove clients when they disconnect.
Dim RMsg() As String
Dim RMsgType() As String
RMsg = Split(Data_, "|:|")
RMsgType = Split(RMsg(0), "|")
If RMsgType(0) = "NEWCA" Then 'New Client Asking for connection
Incomming_Connection(Data_)
End If
End Sub
Public Sub removeClient(ByVal client As ConnectedClient)
If Clients.Contains(client) Then
Clients.Remove(client)
End If
End Sub
Private Function Incomming_Connection(ByVal Data_ As String) As Boolean
Dim RMsg() As String
Dim RMsgType() As String
RMsg = Split(Data_, "|:|")
RMsgType = Split(RMsg(0), "|")
If RMsgType(0) = "NEWCA" Then 'New Client Asking for connection
If RMsgType(1) = "PASSW=1" Then
For I = 1 To UBound(RMsg)
RMsg(I) = Simple_Decrypt(RMsg(I)) 'Just decrypts it if it was encrypted.
Next
End If
Dim Ask_ As Integer
Ask_ = MsgBox(RMsg(1) & " With Hostname " & RMsg(3) & " And Alias " & RMsg(2) & " Is trying to connect to you, do you wish to allow this?", MsgBoxStyle.YesNo, "Allow Connection?")
If Ask_ = vbYes Then
If Len(Add_To_Clients(RMsg(1), RMsg(2), RMsg(3), RMsg(4), RMsg(5))) = 0 Then Incomming_Connection = True
End If
End If
Public Function Add_To_Clients(ByVal IP_ As String, ByVal Alias_ As String, ByVal Hostname_ As String, ByVal RPort_ As String, Optional ByVal Client_Pass As String = "", Optional ByVal ConID As Long = 0) As String
' A DatagridView is being used as the database to store everything.
If IP_ = "" Then
Add_To_Clients = "No Specified IP"
Exit Function
End If
If RPort_ = "" Then
Add_To_Clients = "Invalid Remote Port"
Exit Function
End If
If Hostname_ = "" Then
Add_To_Clients = "Invalid Hostname"
Exit Function
End If
Dim I_ As Integer
I_ = 0
Dim AlreadyExists As Integer
AlreadyExists = -1
For I_ = 0 To (ClientLst.Rows.Count - 1)
If ClientLst.Rows(I_).Cells(0).Value = TxtIP.Text Then
AlreadyExists = I_
End If
Next
'Check for duplicate server IDs
If Trim(ClientLst.Rows(0).Cells(0).Value) = "" Then
If AlreadyExists >= 0 Then
ClientLst.Rows(0).Cells(1).Value = IP_
ClientLst.Rows(0).Cells(2).Value = Alias_
ClientLst.Rows(0).Cells(3).Value = Hostname_
ClientLst.Rows(0).Cells(4).Value = RPort_
ClientLst.Rows(0).Cells(5).Value = Client_Uses_Pass(Client_Pass)
ClientLst.Rows(0).Cells(6).Value = Client_Pass
ClientLst.Rows(AlreadyExists).Dispose()
Else
ClientLst.Rows.Add()
ClientLst.Rows(0).Cells(1).Value = IP_
ClientLst.Rows(0).Cells(2).Value = Alias_
ClientLst.Rows(0).Cells(3).Value = Hostname_
ClientLst.Rows(0).Cells(4).Value = RPort_
ClientLst.Rows(0).Cells(5).Value = Client_Uses_Pass(Client_Pass)
ClientLst.Rows(0).Cells(6).Value = Client_Pass
End If
Else
If AlreadyExists <= -1 Then
ClientLst.Rows.Add()
For D_ = 0 To (ClientLst.Rows.Count - 1)
If Trim(ClientLst.Rows(D_).Cells(0).Value) = "" Then
ClientLst.Rows(D_).Cells(1).Value = IP_
ClientLst.Rows(D_).Cells(2).Value = Alias_
ClientLst.Rows(D_).Cells(3).Value = Hostname_
ClientLst.Rows(D_).Cells(4).Value = RPort_
ClientLst.Rows(D_).Cells(5).Value = Client_Uses_Pass(Client_Pass)
ClientLst.Rows(D_).Cells(6).Value = Client_Pass
Exit For
End If
Next D_
Else
Add_To_Clients = "Client Already Exists"
End If
End If
Add_To_Clients = ""
End Function
Hope that i included everything necessary.
-
Mar 24th, 2008, 07:26 AM
#2
Re: [2008] TCP Sockets
Im having a bit of trouble understanding the problem... is it the "general design" you cant figure out? You must keep a collection of connected hosts, and continously listen for incoming connections. This is basically what you've already got in that code.
-
Mar 24th, 2008, 08:51 AM
#3
Thread Starter
Fanatic Member
Re: [2008] TCP Sockets
Yes, it's the general design. I'm having trouble understanding how it can be coded in VB.NET.
I know that the clients are stored in Dim Clients As New List(Of ConnectedClient), and to add more parameters (If needed), i just make it a dictionary. But this data needs to be displayed to the user, organized (To avoid, or allow duplicate IP addresses for example) and used to send data to clients. I can't make the data (Client As Net.Sockets.TcpClient) into something that i can manipulate or understand how to reference to it. And after that send information to a client based on their IP, or Client As Net.Sockets.TcpClient. I plan not to use a dictionary and use Cell 0 of the Datagridview (Which is hidden) for Client As Net.Sockets.TcpClient, and convert the datatypes as needed.
I provided code to show what i have done so far.
-
Mar 24th, 2008, 12:42 PM
#4
Re: [2008] TCP Sockets
Actually, a dictionary doesn't sound like the right way to go for me. It sounds like you want a class. The class would hold the identifying characteristics of a server. I call it a server, because each program will be a server, listening for incoming calls, but each will also maintain a list of others, and when they call them, they will call as a client. That list can be maintained and built as new clients call in to the server portion of the program. The class will contain all the information necessary to create an endpoint such that it can expose a method that takes a message as an argument, establishes a connection, and sends the data.
This may be a misunderstanding of what you are trying to accomplish. It seems to me that the whole design could be that a server is listening, when an incoming connection request is received, it accepts the connection, gets the remote endpoint from the connection, checks to see if it is on the list of known communicants (and adds it if it is not), then either just takes the message and drops the connection, or takes the message, returns a reply, then drops the connection. That last part might not be ideal, but most of my experience is with UDP. My thinking is that if each server is accepting a connection, receiving a message, then dropping the connection, the tasks are pretty simple, as you are not maintaining the state of a series of connections. Each client establishes a connection, sends a message, then exits. What I don't know is how much time is spent establishing a connection in TCP. If that time is insignificant, then this model would work well, because you don't have to know what the connection status is with any given client.
Alternatively, when you accept a connection in the server side, you get a new socket. Check the list of classes to see if that particular client is on the list, and if it is not, create it, while passing the new socket to the class. If the client is already on the list, then just pass the new socket to the client. All communication over that socket is encompassed by the class. The class maintains the socket as long as the connection is maintained, but even after the socket is destroyed, the endpoint information for that client is maintained in the class such that a new communication can be established as needed.
My usual boring signature: Nothing
 
-
Mar 24th, 2008, 12:55 PM
#5
Frenzied Member
Re: [2008] TCP Sockets
So when you create a New 'ConnectedClient' object, is that a new thread in itself?
I've done client/server in java, and I had to create a class that extends the thread class for my listening function and each client.
Just wanted to see.
Thanks,
Justin Fox
-
Mar 24th, 2008, 01:46 PM
#6
Re: [2008] TCP Sockets
 Originally Posted by MonkOFox
So when you create a New 'ConnectedClient' object, is that a new thread in itself?
I've done client/server in java, and I had to create a class that extends the thread class for my listening function and each client.
Just wanted to see.
Thanks,
Justin Fox
Yes each ConnectedClient class reads from its networkstream on a separate thread.
-
Mar 24th, 2008, 02:36 PM
#7
Frenzied Member
Re: [2008] TCP Sockets
Sweet! That makes things a lot easier!
Thanks,
Justin Fox
-
Mar 25th, 2008, 07:45 AM
#8
Thread Starter
Fanatic Member
Re: [2008] TCP Sockets
 Originally Posted by Shaggy Hiker
Actually, a dictionary doesn't sound like the right way to go for me. It sounds like you want a class. The class would hold the identifying characteristics of a server. I call it a server, because each program will be a server, listening for incoming calls, but each will also maintain a list of others, and when they call them, they will call as a client. That list can be maintained and built as new clients call in to the server portion of the program. The class will contain all the information necessary to create an endpoint such that it can expose a method that takes a message as an argument, establishes a connection, and sends the data.
This may be a misunderstanding of what you are trying to accomplish. It seems to me that the whole design could be that a server is listening, when an incoming connection request is received, it accepts the connection, gets the remote endpoint from the connection, checks to see if it is on the list of known communicants (and adds it if it is not), then either just takes the message and drops the connection, or takes the message, returns a reply, then drops the connection. That last part might not be ideal, but most of my experience is with UDP. My thinking is that if each server is accepting a connection, receiving a message, then dropping the connection, the tasks are pretty simple, as you are not maintaining the state of a series of connections. Each client establishes a connection, sends a message, then exits. What I don't know is how much time is spent establishing a connection in TCP. If that time is insignificant, then this model would work well, because you don't have to know what the connection status is with any given client.
Alternatively, when you accept a connection in the server side, you get a new socket. Check the list of classes to see if that particular client is on the list, and if it is not, create it, while passing the new socket to the class. If the client is already on the list, then just pass the new socket to the client. All communication over that socket is encompassed by the class. The class maintains the socket as long as the connection is maintained, but even after the socket is destroyed, the endpoint information for that client is maintained in the class such that a new communication can be established as needed.
Yes, this is what i mean. I was thinking about designing the code that's connectionless, but still on the TCP protocol. This however is not what TCP was designed for. I was going to make a connection to an IP address on a specific port, send the message and then disconnect. But for some reason, this just doesn't feel right. This is why i was going to make it a normal TCP connection, where it connects once, and uses those connections to send the data by. This could also be useful to find out if a client is still online without pinging them in some sort of fashion. If i use the connectionless way, then extra code will be needed on the server to verify that it's a client on their "contact list", It will also be harder to keep 2 clients synchronized, especially if one gets improperly disconnected, also error checking would have to be done to make sure the message got through properly, but it is certainly possible to do this.
Which way would be the better way to go?
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|