VBNET:Sockets

From GPWiki

Contents

Network Sockets

(It is easier to use the TcpListener and TcpClient classes than this)

This will be an article on sending and recieving data over networks, to and from a client or server.

the article will be based off of a sockets library that i have written using the system.net.sockets.socket class.

you may download the source here: Files:Socket.zip or, for a more current source written in c#: Files:Socket.cs.2.0.0.zip


the file above contains a bit more code in it then what i am going to explain. it is the result of what has evolved over years of testing for me, and in all of the projects i have used. it has quite a bit of error handling in it, and also uses events to pass data to your project. its not finished though, but i feel it is ready to be released, sort of like a version 1.0 i guess you could say. if you have any questions, you may email me at chemisus@gmail.com or ask on our message boards. now on to the good stuff.


if you have ever attempted to write your own socket, you may have messed around with the System.Net.Sockets.Socket class before. in doing this, you probably noticed a certain set of functions such as BeginAccept, EndAccept, BeginRecieve, EndRecieve, and so on. well, were going to learn just exactly how to use these.

The socket

the reason why this is first, is because it is the backbone for each socket. before we can do anything, we need to first declare our socket

Dim mySocket as System.Net.Sockets.Socket

this is great and all, but we need to set up our socket to tell the operating system exactly what kind of socket it is.

mySocket = New System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork _
                                  , System.Net.Sockets.SocketType.Stream _
                                  , System.Net.Sockets.ProtocolType.Tcp)

there are many other socket types, but this will create a tcp socket for us (you can also change tcp to udp, if thats what you want)

StateObject

If you have ever done multithreading, you might have noticed that you cant exactly pass data directly to a thread. You have to create a class or structure and stick it in a field until that thread picks it up. Thats pretty much what the StateObject is used for, except that its not kept in your class.

This is the code you will need in order to: accept, send data, recieve data

public class StateObject
     public Socket as System.Net.Sockets.Socket
     public Size as Integer = 32767
     public Buffer(32767) as Byte
 end class

Clients

This is outlined as clients, but really its more then just that. I am combining clients with sending and recieving data, even though these two elements are also used in sessions. the reason i am doing this is so we can connect to a server, and request for data to be sent back.

when you connect to a server, you will be using two functions for this:

BeginConnect
EndConnect

heres how they work together:

Public Sub Connect()
     dim State as New StateObject()
     mySocket.BeginConnect(new System.Net.IPEndPoint(System.Net.Dns.Resolve("www.google.com").AddressList(0) _
                                                     ,80) _
                           , AddressOf EndConnect _
                           , State)
 End Sub
 
 Public Sub EndConnect(AR as System.IAsyncResult)
     mySocket.EndConnect(AR)
 End Sub

and now you are connected.

so now that you are connected, you may want to allow the connection to recieve data. i generally stick this in another function and call it right after the client connects or the session accepts. it looks like this:

Public Sub Recieve()
     Dim State as New StateObject()
     mySocket.BeginReceive(State.Buffer _ 
                           , 0 _
                           , State.Size _ 
                           , System.Net.Sockets.SocketFlags.None _ 
                           , addressof EndReceive _ 
                           , State)
 End Sub
 
 Public Sub EndReceive(AR as IAsyncResult)
     Dim State as StateObject = ctype(AR, StateObject)
     Dim Length as Integer = mySocket.EndReceive(AR)
     System.Diagnostics.Debug.WriteLine(System.Text.Encoding.ASCII.GetString(State.Buffer, 0, Length))
     Receive() ' call this to set up receiving again.
 End Sub

if that works, we should receive data and debug should display it whenever the server sends us something.

now, to send data!

Public Sub Send(Data() as Byte)
     Dim State as New StateObject()
     mySocket.BeginSend(Data, 0, Data.Length, Sockets.SocketFlags.None, addressof EndSend, State)
 End Sub
 
 Public Sub EndSend(AR as IAsyncResult)
     Dim Length as Integer
     Length = mySocket.EndSend(AR)
     System.Diagnostics.Debug.WriteLine("Sent: " & Length.ToString())
 End Sub

so, if we connect to www.google.com on port 80, and do the following:

Send("GET / HTTP/1.1" & vbNewLine & "Host: www.google.com" & vbNewLine & vbNewLine)

we SHOULD get a web page back. please note, i have typed all of this in this text box, so theres a slight chance i might have made a typo somewhere.

Server

whew! now that was pretty much the hard part. (error handling is a bit harder) but on to some easy stuff.

servers pretty much just listen for a connection, and then once a connection is requested, it passes the socket on to a session. (yes, you can combine all of this into one socket like the winsock control from vb6, but its easier to break them down into 3 components)

how to open a connection:

Public Sub Listen(Port as Integer, Backlog as Integer)
     mySocket.Bind(System.Net.IPAddress.Any, Port)
     mySocket.Listen(Backlog)
     mySocket.BeginAccept(AddressOf Request, mySocket)
 End Sub
 
 Public Sub Request(AR as IAsyncResult)
     System.Diagnostics.Debug.Writeline("Connection Requested.")
     ' Accept(AR) ' use this with sessions
     mySocket.BeginAccept(AddressOf Request, mySocket)
 End Sub

note the function "Accept" this will be used with sessions to accept a connection.

Sessions

Now, server and session sockets go hand in hand. servers listen while sessions accept. the purpose of this is so you can just have one server listening, and array of sessions to work with.

heres how to accept a connection with a session:

Public Sub Accept(AR as IAsyncResult)
     Dim pSocket as System.Net.Sockets.Socket = ctype(AR.AsyncState, System.Net.Sockets.Socket)
     mySocket = pSocket.EndAccept(AR)
     ' here you would want to start recieving also
 End Sub

and thats pretty much it.

Comments

This is my first tutorial that ive tried to write, and i am willing to admit, its probably hard to understand at first. if you have any questions, just email me. this should be open to people to edit also, i have no problems with that, or else i wouldnt have been posting this here =P

Resources

Lidgren.Library.Network (Network library for Reliable UDP in C#)