Understanding Sockets

Arokh

Centurion
Joined
Apr 11, 2006
Messages
124
Hi
Since I made myself comfortable in VB 05 and it is going well so far,
I want to give a try to make some chatclient & -server.

So far I accomplished, by reading through the forum and the msdn, to make a TcpListener and TcpClient by copying the code. I already made some experiments with it and changed the code to make it more useful for my purposes.

Visual Basic:
Imports System
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports Microsoft.VisualBasic

Public Class frmClient
    Public Client As New TcpClient
    Public Stream As NetworkStream

    Private Sub Connect(ByVal server As [String], ByVal port As Int32)
        Try
            Client.Connect(server, port)
            Stream = Client.GetStream
        Catch e As ArgumentNullException
            Debug.Print("ArgumentNullException: {0}", e)
        Catch e As SocketException
            Debug.Print("SocketException: {0}", e)
        End Try
        Debug.Print(ControlChars.Cr + " Press Enter to continue...")
    End Sub

    Private Sub SendMsg(ByVal Message As String)
         Dim data As [Byte]() = System.Text.Encoding.ASCII.GetBytes(Message)
        Stream.Write(data, 0, data.Length)

        Debug.Print("Sent: {0}", Message)
    End Sub

    Private Sub CloseConnection()
        Client.Close()
    End Sub

    Private Function RecieveMsg() As String
        Dim Data = New [Byte](256) {}

        Dim responseData As [String] = [String].Empty

        Dim bytes As Int32 = Stream.Read(Data, 0, Data.Length)
        responseData = System.Text.Encoding.ASCII.GetString(Data, 0, bytes)
        Debug.Print("Received: {0}", responseData)
        RecieveMsg = responseData
    End Function

    Private Sub frmClient_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Connect("127.0.0.1", 13000)
        SendMsg("Bla")
        RecieveMsg()
        CloseConnection()
    End Sub
End Class
Visual Basic:
Imports System
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports Microsoft.VisualBasic

Public Class frmServer
    Public stream As NetworkStream
    Public Sub Main()
        Try
            Dim port As Int32 = 13000
            Dim localAddr As IPAddress = IPAddress.Parse("127.0.0.1")
            Dim server As New TcpListener(localAddr, port)
            server.Start()
            Dim bytes(1024) As [Byte]
            Dim data As [String] = Nothing
            While True
                Debug.Print("Waiting for a connection... ")
                Dim client As TcpClient = server.AcceptTcpClient()
                Debug.Print("Connected!")
                data = Nothing
                stream = client.GetStream()
                Dim i As Int32

                i = stream.Read(bytes, 0, bytes.Length)
                While (i <> 0)
                    data = System.Text.Encoding.ASCII.GetString(bytes, 0, i)
                    Debug.Print([String].Format("Received: {0}", data))
                    data = data.ToUpper()
                    Dim msg As [Byte]() = System.Text.Encoding.ASCII.GetBytes(data)
                    stream.Write(msg, 0, msg.Length)
                    Debug.Print([String].Format("Sent: {0}", data))

                    i = stream.Read(bytes, 0, bytes.Length)

                End While

            End While
        Catch e As SocketException
            Debug.Print("SocketException: {0}", e)
        End Try

        Debug.Print(ControlChars.Cr + "Hit enter to continue...")
        Console.Read()
    End Sub 'Main
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Main()
    End Sub
End Class

With this I could connect to the TCPListener send some message,
then the Listener would send a message back and the client recieves it.
But this only works when each side gets an answer otherwise it waits forever.
Since I have to be able to get and send messages, for a Chatclient, simutanously
this isn't the best solution :).
I've read that I have to make an asynchronous connection,
but I couldn't find enough information to get me started.

So besides some tutorials you could link me to, I have some questions:
1. If I make an asynchronous connection can I recieve and send data/text with only one open port from more then one IP?
I guess thats the case since for programs like mIRC, µTorrent only need one port.
Is that only possible asynchronous connection?
2. What is the main reason for a Synchronous connection? (File Transfering?)
3. Does stream.Read(bytes, 0, bytes.Length) return the value of bytes still unread in the stream?
If so why doesn't it leave the loop?
4. What happens after Stream.Write(data, 0, data.Length) is called in the client?
Does it send the data/text right through the servers port?
For example when the client send a message, closes the connection before the server has recieved the message, what happens?
5. When the server "sends" a message, does it actually send it or does the client look after such messages and pulls them from the server?
 
Typically you would use threading to get round the problem of blocking, a single listener would wait for incomming connections and spawn a new thread to handle the communication - search these forums and you should find some examples.
Regardless of asynchronus or synchronus connections if you are using TCP then it is a single IP/Port to a single IP/Port - there isn't a way to have multiple connections using the same port (this isn't the same if you are using UDP based broadcasts or multicast packets though).

The main reason for synchronus connections is simplicity - you do not have to worry about responding to incomming data etc.

Stream.Read returns the number of bytes read into the buffer - for network connections there is no simple way to determine how much data is left as the other end could keep adding more data anyway...

Stream.Write sends the data to the underlying connection, closing the stream will flush any unsent data...

http://msdn2.microsoft.com/en-US/library/system.io.stream(VS.80).aspx may be worth a read regarding the Stream class.
http://www.cisco.com/warp/public/535/4.html and http://www.doc.ic.ac.uk/~ih/doc/pc_conn/tcpip/intro/intro0.html are probably also worth reading as they will answer some of the networking questions.
 
Thank you for answering all my questions.

But I'm quiet confused with this statement:
Regardless of asynchronus or synchronus connections if you are using TCP then it is a single IP/Port to a single IP/Port - there isn't a way to have multiple connections using the same port

Because a Program like µTorrent (Bittorrent client) only uses one port to communicate with several other connections.
At least I think so, I set µTorrent up to a specific port and forwarded that port so the firewall allows incoming connections.
The other ports are blocked. So wouldn't that mean only one other client could simutanously send me data (which isn't the case)?

I've downloaded a program that lists all active ports and it only confused me more.
It showed that more than one client is connected to my forwarded port (7525).
The odd thing is it also shows ports which I didn't forwarded and should be blocked.

Code:
... (all TCP connections)
utorrent.exe	192.168.1.2:7525	218.212.14.4:62452	ESTABLISHED	
utorrent.exe	192.168.1.2:7525	218.212.11.113:2739	ESTABLISHED
utorrent.exe	192.168.1.2:7525	219.129.49.132:2633	ESTABLISHED
utorrent.exe	192.168.1.2:7525	220.127.9.33:2327	ESTABLISHED
utorrent.exe	192.168.1.2:7525	220.236.148.182:61584	ESTABLISHED
utorrent.exe	192.168.1.2:7525	220.239.223.166:2960	ESTABLISHED
utorrent.exe	192.168.1.2:4899	198.166.13.26:6346	ESTABLISHED
utorrent.exe	192.168.1.2:4264	86.104.211.80:32459	ESTABLISHED
utorrent.exe	192.168.1.2:3541	82.83.102.18:6881	ESTABLISHED
utorrent.exe	192.168.1.2:3225	220.253.23.182:49153	ESTABLISHED
...

It seems I have a completely wrong picture of how these things work, which is giving me a hard time.
I thought that only one port is needed to establish a connection and to send and recieve data.
But now I understand it is needed to have one open port on each side?
Something like this?
Code:
----						 	      ----
|PC|-->Outgoing Port(1234)-->Internet-->Incoming Port(5678)-->|PC|
----						 	      ----
Does the direction matter, is the following also correct on the same connection?
----						 	      ----
|PC|<--Incoming Port(1234)<--Internet<--Outgoing Port(5678)<--|PC|
----						 	      ----
Since I thought only the incoming port is necessary, how is the incoming port chosen?
When the port is randomly chosen and it is coincidently the port I have set for µTorrent and I start µTorrent,
does that mean µTorrent can't use the port anymore?


Back to the praktical part:
When I understand you correctly, the server listens for incoming connections,
then if it gets one, it tells the client to connect to different port (given by the server),
after that the initial connection is closed so the server can listen for other connection requests.

Since I understand it there is no incoming or outgoing port, it just tells if data is recieved oder sent,
it would mean that the server has to have a great range of ports open and the client has also to forward
a port to be able to send data to the server instead of just to be able to recieve it.
Because a firewall blocks all incoming traffic by default.

I've made a little draft to show how I would do the threading on the server- and clientside,
just to be sure it is the right way to go. Since I've never programmed any applications with multithreading
I've been reading on it in the msdn. Now I'm experimening with it.

Network.png
 
The port a process 'listens' on is how a client will make it's initial connection, i.e. a web server listens (by default anyway) on port 80. When a client needs to communicate with the server it will make it's initial connection to the server on this port.
As part of the connection handshake the client and server will negotiate another set of port numbers to communicate on and these are the ports used for the remainder of the conversation.

These 'negotiated' port numbers are not however treated as new connections as they are part of the existing connection that happened to start on port 80 - therefore they are not blocked by your firewall etc. as they are not 'incoming requests'.

The scenario I suggested above of using seperate threads for each connection will work but this can cause scalability issues if the number of connections gets large as you will end up with a large number of threads, a lot of which will be idle for large portions of the time.
In this scenario rather than a 'one client = one thread' model you would find using async sockets more scalable as you would only need to allocate resources as and when data arrives.
 
Back
Top