Sending an receiving two types of data across TCP connection - vb.net

On the Client I want to -
I want to send a screenimage from the client and receive a string with cursor coordinates in.
On the server I want to receive and display the screenimage on Form2
and then send back my cursor coordinates to the client.
I can successfully send text messages from the client and receive them on the server using this code -
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
senddata("CHAT|" & TextBox3.Text) 'send the data with CHAT| as header
TextBox4.Text &= "You: " & " " & TextBox3.Text.Split("|")(0) & vbNewLine
End Sub
Sub senddata(ByVal message As String)
Dim sw As New StreamWriter(t.GetStream) 'declare a new streamwriter
sw.WriteLine(message) 'write the message
sw.Flush()
End Sub
and receive it on the server with this code -
Imports System.IO
Imports System.Net.Sockets
Public Class ConnectedClient
Private cli As TcpClient 'decleare a tcp client which will be the client that we assign to an instance of this class
Private uniqueid As String 'this will be used for the name property
Public Property name ''This will be the name of the ID containing its Unique ID
Get
Return uniqueid 'when we want to get it, it will return the Unique ID
End Get
Set(ByVal value)
uniqueid = value 'Used for setting the name
End Set
End Property
Sub New(ByVal client As TcpClient)
Dim r As New Random 'create a new random to serve as way to create our unique ID
Dim x As String = String.Empty 'declare a new variable to hold the ID
For i = 0 To 7 'we are goign to have an ID of 7 randomly generated characters
x &= Chr(r.Next(65, 89)) 'create a generate dnumber between 65 and 89 and get the letter that has the same ascii value (A-Z)
' and add it onto the ID string
Next
Me.name = client.Client.RemoteEndPoint.ToString().Remove(client.Client.RemoteEndPoint.ToString().LastIndexOf(":")) & " - " & x 'set the name to the Unique ID
cli = client 'assign the client specified to the TCP client variable to we can operate with it
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'start reading using the read subroutine
End Sub
Public Event gotmessage(ByVal message As String, ByVal client As ConnectedClient) 'this is raised when we get a message from the client
Public Event disconnected(ByVal client As ConnectedClient) 'this is raised when we get the client disconnects
Sub read(ByVal ar As IAsyncResult) 'this will process all messages being recieved
Try
Dim sr As New StreamReader(cli.GetStream) 'initialize a new streamreader which will read from the client's stream
Dim msg As String = sr.ReadLine() 'create a new variable which will be used to hold the message being read
RaiseEvent gotmessage(msg, Me) 'tell the server a message has been recieved. Me is passed as an argument which represents
' the current client which it has recieved the message from to perform any client specific
' tasks if needed
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'continue reading from the stream
Catch ex As Exception
Try 'if an error occurs in the reading purpose, we will try to read again to see if we still can read
Dim sr As New StreamReader(cli.GetStream) 'initialize a new streamreader which will read from the client's stream
Dim msg As String = sr.ReadLine() 'create a new variable which will be used to hold the message being read
RaiseEvent gotmessage(msg, Me) 'tell the server a message has been recieved. Me is passed as an argument which represents
' the current client which it has recieved the message from to perform any client specific
' tasks if needed
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'continue reading from the stream
Catch ' IF WE STILL CANNOT READ
RaiseEvent disconnected(Me) 'WE CAN ASSUME THE CLIENT HAS DISCONNECTED
End Try
End Try
End Sub
Sub senddata(ByVal message As String) 'this is used to deal with sending out messages
Dim sw As New StreamWriter(cli.GetStream) 'declare a new streamwrite to write to the stream between the client and the server
sw.WriteLine(message) 'write the message to the stream
sw.Flush()
End Sub
Sub listen(ByVal port As Integer)
Try
Dim t As New TcpListener(IPAddress.Any, port) 'declare a new tcplistener
t.Start() 'start the listener
Do
Dim client As New ConnectedClient(t.AcceptTcpClient) 'initialize a new connected client
AddHandler client.gotmessage, AddressOf recieved 'add the handler which will raise an event when a message is recieved
AddHandler client.disconnected, AddressOf disconnected 'add the handler which will raise an event when the client disconnects
Loop Until False
Catch
End Try
End Sub
In a totally different application I can successfully send the screen image from the client using this code
Imports System.Net.Sockets
Imports System.Threading
Imports System.Drawing
Imports System.Runtime.Serialization.Formatters.Binary
Public Class Form1
Dim client As New TcpClient
Dim ns As NetworkStream
Dim port As Integer
Dim server As TcpListener
Dim listening As New Thread(AddressOf Listen)
Dim GetImage As New Thread(AddressOf ReceiveCursor)
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles btnConnect.Click
port = CInt(txtPort.Text)
Try
If btnConnect.Text = "Connect" Then
client.Connect(txtIP.Text, port)
'MsgBox("Client connected !")
Timer1.Start()
btnConnect.Text = "Disconnect"
Else
btnConnect.Text = "Connect"
Timer1.Stop()
End If
Catch ex As Exception
MsgBox("Failed to connect..." + ex.Message)
End Try
End Sub
Public Function Desktop() As Image
Dim bounds As Rectangle = Nothing
Dim screenshot As System.Drawing.Bitmap = Nothing
Dim graph As Graphics = Nothing
bounds = Screen.PrimaryScreen.Bounds
screenshot = New Bitmap(bounds.Width, bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
graph = Graphics.FromImage(screenshot)
graph.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size, CopyPixelOperation.SourceCopy)
Return screenshot
End Function
Private Sub SendDesktop()
Dim bf As New BinaryFormatter
ns = client.GetStream
bf.Serialize(ns, Desktop())
End Sub
Private Sub BtnShare_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnShare.Click
If btnShare.Text.StartsWith("Share") Then
Timer1.Start()
'port = Integer.Parse(Me.txtPort.Text)
'server = New TcpListener(port)
'listening.Start()
btnShare.Text = "Stop Sharing"
Else
Timer1.Stop()
btnShare.Text = "Share Desktop"
End If
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
SendDesktop()
' SetCursorPosition()
End Sub
Private Sub ReceiveCursor()
Dim bf As New BinaryFormatter
While client.Connected = True
ns = client.GetStream
txtCursorPosition.Text = bf.Deserialize(ns)
End While
End Sub
Private Sub Listen()
While client.Connected = False
server.Start()
client = server.AcceptTcpClient
End While
GetImage.Start()
End Sub
Public Sub StopListening()
GetImage.Abort()
server.Stop()
client = Nothing
If listening.IsAlive Then
listening.Abort()
End If
End Sub
End Class
and receive it on the server using this code -
Imports System.Net.Sockets
Imports System.Threading
Imports System.Drawing
Imports System.Runtime.Serialization.Formatters.Binary
Public Class Form2
Dim port As Integer
Dim client As New TcpClient
Dim ns As NetworkStream
Dim server As TcpListener
Dim listening As New Thread(AddressOf Listen)
Dim GetImage As New Thread(AddressOf ReceiveImage)
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Start the timer, obtain the port and start listening
Timer1.Start()
port = Integer.Parse(Form1.txtPort.Text)
server = New TcpListener(port)
listening.Start()
End Sub
Private Sub ReceiveImage()
Dim bf As New BinaryFormatter
While client.Connected = True
ns = client.GetStream
PictureBox1.Image = bf.Deserialize(ns)
End While
End Sub
Private Sub Listen()
While client.Connected = False
server.Start()
client = server.AcceptTcpClient
End While
GetImage.Start()
End Sub
Public Sub StopListening()
GetImage.Abort()
server.Stop()
client = Nothing
If listening.IsAlive Then
listening.Abort()
End If
End Sub
End Class
Is there a way of doing both using the same connection and somehow detecting which is text and which is the screenimage?

Related

How reconnect clients sockets to my server if my server restarts

I'm trying to make a client-server connection using sockets.
It basically works pretty well; only one problem is when I close and re-open my server on server computer the clients won't reconnect
This is my client code :
Imports System.Net.Sockets
Imports System.Threading
Imports System.IO
Public Class Form1
Public Delegate Sub MessageReceivedEventHandler(ByVal sender As Object, ByVal e As MessageReceivedEventArgs)
Public Delegate Sub ClientConnectedEventHandler(ByVal sender As Object, ByVal e As ClientConnectedEventArgs)
Public Event MessageReceived As MessageReceivedEventHandler
Public Event ClientConnected As ClientConnectedEventHandler
Public Buffer As Byte()
Public Shared Client As TcpClient
Dim Port As Integer = 5050
Dim host As String = "computer name"
Dim KEY As String = "mykey"
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim T As New Thread(AddressOf run)
T.Start()
End Sub
Private Sub run()
' Connect to the server'
Client = New TcpClient(host, Port)
Dim T As New Thread(AddressOf Receiver, 10)
T.Start()
End Sub
Public Shared Function StringToByte(ByVal STR As String) As Byte()
Return System.Text.Encoding.Default.GetBytes(STR)
End Function
Public Shared Function ByteToString(ByVal BYS As Byte()) As String
Return System.Text.Encoding.Default.GetString(BYS)
End Function
Public Sub Data(ByVal Sender As Object, ByVal Data As MessageReceivedEventArgs) Handles Me.MessageReceived
Dim info As Byte() = Data.Message
Dim Client As Socket = Data.clientSocket
Dim ID As String = Data.Sender
Dim Arr As String() = Split(System.Text.Encoding.Default.GetString(info), "mykey")
Try
Select Case Arr(0)
Case "MSG"
MsgBox(Arr(1))
End Select
Catch ex As Exception
MsgBox("Error Detected in Data Client :" & ex.Message)
End Try
End Sub
Sub Receiver()
Dim M As New MemoryStream
Try
If Client.Available > 0 Then
ReDim Buffer(Client.Available - 1)
Client.Client.Receive(Buffer, 0, Buffer.Length, SocketFlags.None)
M.Write(Buffer, 0, Buffer.Length)
If System.Text.Encoding.Default.GetString(M.ToArray).Contains("EOF") Then
Dim Data As Byte() = (M.ToArray).Remove(ConfigTcp.ENDOFPACKET)
Dim Msg As New MessageReceivedEventArgs
Msg.Message = Data
Msg.clientSocket = Client.Client
Msg.Sender = Client.Client.Handle.ToString()
RaiseEvent MessageReceived(Me, Msg)
M.Dispose()
M = New MemoryStream
End If
End If
Thread.Sleep(1)
Catch ex As Exception
MsgBox("Error Detected :" & ex.Message)
End Try
End Sub
Public Shared Sub Send(ByVal sock As Socket, ByVal s As String)
Send(sock, System.Text.Encoding.UTF8.GetBytes(s))
End Sub
Private Shared Sub Send(ByVal sock As Socket, ByVal b As Byte())
Try
Dim Memory As MemoryStream = New MemoryStream
Dim CB As Byte() = b
Memory.Write(CB, 0, CB.Length)
Memory.Write(ConfigTcp.ENDOFPACKET, 0, ConfigTcp.ENDOFPACKET.Length)
sock.Send(Memory.ToArray, 0, Memory.Length, SocketFlags.None)
Memory.Dispose()
Catch x As Exception
End Try
End Sub
End Class
Public Class MessageReceivedEventArgs
Inherits EventArgs
Public Sender As String
Public clientSocket As Socket
Public Message As Byte()
End Class
Public Class ClientConnectedEventArgs
Inherits EventArgs
Public clientID As String
Public clientSocket As Socket
End Class
Your client needs to handle disconnects and reconnect if needed.
To accomplish this, you can catch a SocketException and see why the socket was closed.
Depending on the exception you can run a method to have your client reconnect.
Public Sub DoReconnect()
Dim SecondsToWait as Integer=1
Do
Try
Client = New TcpClient(host, Port)
Exit Do
Catch SockEx as SocketException
'TODO: decide whether to attempt reconnection or exit
Catch Ex as Exception
'TODO: Most likely should terminate the program here
Console.WriteLine(Ex.ToString())
Exit Do
End Try
System.Threading.Thread.Sleep(SecondsToWait*1000)
SecondsToWait <<=1 ' Exponential Back Off Time
Loop
End Sub
#Alexander Higgins
I tried something like this but seems that SocketException happens only before opening the server for the first time.If I close the server it and re-open it, it doesn't catch SocketException
Public Sub DoReconnect()
re:
Thread.Sleep(1000)
Try
Client = New TcpClient(host, Port)
Dim T As New Thread(AddressOf Receiver, 10)
T.IsBackground = True
T.Start()
Catch SockEx As SocketException
MsgBox("SocketException " + SockEx.Message)
GoTo re
Catch Ex As Exception
MsgBox("Exception " + Ex.Message)
End Try
End Sub
Private Sub run()
DoReconnect()
End Sub

Send and receive data from a websocket server using sockets in Vb.net

i am new to network programming and would really appreciate your help to receive and send data to a server. I am trying to Use Sockets to Send and Receive data from a server which is developed using WebSockets.
Until now i have created the following Code which i think is wrong: in this code i am trying to send data on a normal thread but receive data on a different thread so that the program dont go on hold.
as i send data to the server, after some time i get this text reply on the textbox in which i am trying to receive the server response:
HTTP/1.1 501 Not Implemented
Any help will be much appreciated
TCPControl class(handles the connection, send and receive matters)
Public Class TCPControl
Public client As TcpClient
Public DataStream As StreamWriter
Private ReceiveData As StreamReader
Private comThread As Thread
Public isListening As Boolean = True
Public Event MessageReceived(sender As TCPControl, Data As String)
Public Sub New(Host As String, Port As Integer)
Try
client = New TcpClient(Host, Port)
DataStream = New StreamWriter(client.GetStream)
comThread = New Thread(New ThreadStart(AddressOf Listening))
comThread.Start()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub Listening()
Do Until isListening = False
If client.Connected = True Then
ReceiveData = New StreamReader(client.GetStream)
End If
Try
RaiseEvent MessageReceived(Me, ReceiveData.ReadLine)
Catch ex As Exception
End Try
Thread.Sleep(10)
Loop
End Sub
Public Sub Send(Data As String)
DataStream.Write(Data & vbCrLf)
DataStream.Flush()
End Sub End Class
Form1 class(handles form load and other stuff)
Public Class Form1
Private client As TCPControl
' Private receiveClient As TCPControlReceive
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' client = New TCPControl("174.129.224.73", 80)
client = New TCPControl(GetIpAddress("echo.websocket.org").ToString, 80)
If client.client.Connected Then Label1.Text = "Connected"
'receiveClient = New TCPControlReceive
AddHandler client.MessageReceived, AddressOf OnLineReceived
End Sub
Private Delegate Sub UpdateMessageDelegate(TB As TextBox, txt As String)
Private Sub UpdateText(TB As TextBox, txt As String)
If TB.InvokeRequired Then
TB.Invoke(New UpdateMessageDelegate(AddressOf UpdateText), New Object() {TB, txt})
Else
If txt IsNot Nothing Then
TB.AppendText(txt & vbCrLf)
End If
End If
End Sub
Private Sub OnLineReceived(sender As TCPControl, Data As String)
UpdateText(txtReceive, Data)
End Sub
Private Function GetIpAddress(address As String) As IPAddress
Dim ips As IPAddress()
ips = Dns.GetHostAddresses(address)
Return ips(0)
End Function
Private Sub SendMessage()
If client.client.Connected = True Then
client.Send(txtSend.Text)
End If
End Sub
Private Sub Form1_ContextMenuStripChanged(sender As Object, e As EventArgs) Handles Me.ContextMenuStripChanged
If client.client.Connected = True Then
client.DataStream.Close()
client.Client.Close()
End If
client.isListening = False
End Sub
Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click
SendMessage()
txtSend.Clear()
End Sub End Class
'HTTP/1.1 501 Not Implemented' was sent by the server, not generated by VB.
I would assume from this that your code works fine, and the problem lies with the command(s) you are sending after the client connects.

Tcp Client/Server - Client messages issue

I have client and server application.
I have the client disconnect from the server with client.close via a disconnect button.
I send a message, shows on server. ok works great.
I disconnect and then reconnect. I send a message. It shows the message two times.
I disconnect another time then reconnect. I send a message. It then shows the message three times.
It is incrementing the message and sending it multiple times after the disconnect and then reconnect.
Help? Been trying to figure this out for a while
[SERVER]
Public Class Server
Dim Listener As TcpListener
Dim Client As TcpClient
Dim ListenerThread As System.Threading.Thread
Dim ClientID As String
Dim ClientIP As String
Dim ClientIPandID As String
Dim ClientIPandPort As String
Dim TotalItemCount As String
Dim clientcount As Integer = 0
Private Sub Server_Load(sender As Object, e As EventArgs) Handles Me.Load
CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub ButtonStart_Click(sender As System.Object, e As System.EventArgs) Handles ButtonStart.Click
If TextBoxPort.Text = "" Then
MsgBox("Please Enter Port To Run On.")
Else
ListenerThread = New System.Threading.Thread(AddressOf Listening)
ListenerThread.IsBackground = True
ListenerThread.Start(TextBoxPort.Text)
ButtonStart.Enabled = False
ButtonStop.Enabled = True
ListBox1.Items.Add("[SERVER] Running on Port " + TextBoxPort.Text)
ListBox1.Items.Add("[SERVER] Waiting For A Connection...")
End If
End Sub
Private Sub Listening(ByVal Port As Integer)
Try
Listener = New TcpListener(IPAddress.Any, Port)
Listener.Start()
Do
Client = Listener.AcceptTcpClient 'Accepts Client Trying To Connect
If Client.Connected Then
MsgBox("Client Connected")
End If
clientcount += 1
GetClientInfo() 'Retrieves The Clients Info
AddHandler ReceivedMessage, AddressOf ReceivedMessage1
Loop Until False
Catch ex As Exception
End Try
End Sub
'Events
Public Event ReceivedMessage(ByVal Command As String)
Private Sub GenerateClientSessionNumber()
Dim r As New Random
Dim x As String = String.Empty
For i = 0 To 7
x &= Chr(r.Next(65, 89))
Next
ClientID = x
End Sub
Private Sub GetClientInfo()
GenerateClientSessionNumber()
ClientIPandID = Client.Client.RemoteEndPoint.ToString().Remove(Client.Client.RemoteEndPoint.ToString().LastIndexOf(":")) & " - " & ClientID
ClientIP = Client.Client.RemoteEndPoint.ToString().Remove(Client.Client.RemoteEndPoint.ToString().LastIndexOf(":"))
ClientIPandPort = Client.Client.RemoteEndPoint.ToString()
MsgBox(ClientIPandPort)
ListBox2.Items.Add(ClientIPandID)
Client.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf Reading, Nothing)
End Sub
Private Sub ButtonStop_Click(sender As System.Object, e As System.EventArgs) Handles ButtonStop.Click
Listener.Stop()
Client.Close()
ButtonStop.Enabled = False
ButtonStart.Enabled = True
ListBox1.Items.Add("[SERVER] Server Stopped")
End Sub
Private Sub Reading()
Try
Dim Reader As New StreamReader(Client.GetStream)
Dim Command As String = Reader.ReadLine
Client.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf Reading, Nothing)
RaiseEvent ReceivedMessage(command)
Catch ex As Exception
End Try
End Sub
Private Sub ReceivedMessage1(ByVal Command As String)
Dim Message() As String = Command.Split("|")
If Message(0) = "MESSAGE" Then
MsgBox("Message Received From Client " + ">" + Message(1))
End If
end sub
[CLIENT]
Public Class Client
Dim Client As New TcpClient
Sub Connect(ByVal ServerIP As String, ByVal Port As Integer)
'Try To Make Connection With Server
If Client.Connected = True Then
MsgBox("Already Connected")
MsgBox("Connected To " + Client.Client.RemoteEndPoint.ToString)
Else
MsgBox("Currently Not Connected. Trying To Connect...")
Try
Client.Connect(ServerIP, Port)
MsgBox("Connected")
Client.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf Reading, Nothing)
Catch ex As Exception
MsgBox("Could Not Connect To Server. Check Server." + ex.Message.ToString)
End Try
End If
End Sub
Private Sub SendData(ByVal Message As String) 'Sends Data to Server
TextBox1.Text = Message
Try
If Client.Connected = True Then
Dim Writer As New StreamWriter(Client.GetStream)
Writer.WriteLine(Message)
Writer.Flush()
Else
MsgBox("Cannot Send Message. Connection To Server Is Not Active.")
End If
Catch ex As Exception
MsgBox("You Are Not Connected To The Server." + vbCrLf + ex.Message.ToString)
End Try
End Sub
Private Sub ButtonConnect_Click(sender As Object, e As EventArgs) Handles ButtonConnect.Click
Connect(TextBoxIPAddress.Text, TextBoxPort.Text)
End Sub
Private Sub ButtonSendMessage_Click(sender As Object, e As EventArgs) Handles ButtonSendMessage.Click
SendData("MESSAGE|" & TextBoxMessage.Text)
End Sub
Private Sub Client_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub Button7_Click(sender As Object, e As EventArgs) Handles Button7.Click
SendData("DISCONNECT|")
Client.Close()
Client = New TcpClient
End Sub
End Class
i am not sure about the problem but i can give you some hypothesizes, firstly you add handler on every client you connect on server side and that means multiple pointer to the same place, secondly when you connect to server and reconnect you actually don't tell the server that the two clients are the same so he make two channels between the client and the server, the first is the old one that didn't close and he still have handler on it, the server don't recognize that the first is disconnected because he is connected! even if its another object at client. so when client disconnects , disconnect him from server too, or at every loop test if clients are connected before accepting do this test .
Since I have a class Called "ConnectedClient". I was able to make a call to that specific client or all clients and Close/Destroy the connection.

Issue with socket chat not being able to send full messages

I am having a little issue with this socket chat, if I send little messages like "hello world" it works perfectly fine but if I try to send huge messages with about 10,000 characters it just crashes, i've been trying to fix this for the past 3 days with no luck, what should I be doing?
Server source:
Imports System.Text
Imports System.Net
Imports System.Net.Sockets
Enum Command
Login
'Log into the server
Logout
'Logout of the server
Message
'Send a text message to all the chat clients
List
'Get a list of users in the chat room from the server
Null
'No command
End Enum
Public Class Form1
'The ClientInfo structure holds the required information about every
'client connected to the server
Private Structure ClientInfo
Public socket As Socket
'Socket of the client
Public strName As String
'Name by which the user logged into the chat room
End Structure
'The collection of all clients logged into the room (an array of type ClientInfo)
Private clientList As ArrayList
'The main socket on which the server listens to the clients
Private serverSocket As Socket
Private byteData As Byte() = New Byte(1023) {}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
clientList = New ArrayList()
'We are using TCP sockets
serverSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
'Assign the any IP of the machine and listen on port number 1000
Dim ipEndPoint As New IPEndPoint(IPAddress.Any, 2277)
'Bind and listen on the given address
serverSocket.Bind(ipEndPoint)
serverSocket.Listen(4)
'Accept the incoming clients
serverSocket.BeginAccept(New AsyncCallback(AddressOf OnAccept), Nothing)
Catch ex As Exception
MessageBox.Show(ex.Message, "SGSserverTCP", MessageBoxButtons.OK, MessageBoxIcon.[Error])
End Try
End Sub
Private Sub OnAccept(ar As IAsyncResult)
Try
Dim clientSocket As Socket = serverSocket.EndAccept(ar)
'Start listening for more clients
serverSocket.BeginAccept(New AsyncCallback(AddressOf OnAccept), Nothing)
'Once the client connects then start receiving the commands from her
clientSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None, New AsyncCallback(AddressOf OnReceive), clientSocket)
Catch ex As Exception
MessageBox.Show(ex.Message, "SGSserverTCP", MessageBoxButtons.OK, MessageBoxIcon.[Error])
End Try
End Sub
Private Sub OnReceive(ar As IAsyncResult)
Try
Dim clientSocket As Socket = DirectCast(ar.AsyncState, Socket)
clientSocket.EndReceive(ar)
'Transform the array of bytes received from the user into an
'intelligent form of object Data
Dim msgReceived As New Data(byteData)
'We will send this object in response the users request
Dim msgToSend As New Data()
Dim message As Byte()
'If the message is to login, logout, or simple text message
'then when send to others the type of the message remains the same
msgToSend.cmdCommand = msgReceived.cmdCommand
msgToSend.strName = msgReceived.strName
Select Case msgReceived.cmdCommand
Case Command.Login
'When a user logs in to the server then we add her to our
'list of clients
Dim clientInfo As New ClientInfo()
clientInfo.socket = clientSocket
clientInfo.strName = msgReceived.strName
clientList.Add(clientInfo)
'Set the text of the message that we will broadcast to all users
msgToSend.strMessage = "<<<" & msgReceived.strName & " has joined the room>>>"
Exit Select
Case Command.Logout
'When a user wants to log out of the server then we search for her
'in the list of clients and close the corresponding connection
Dim nIndex As Integer = 0
For Each client As ClientInfo In clientList
If client.socket Is clientSocket Then
clientList.RemoveAt(nIndex)
Exit For
End If
nIndex += 1
Next
clientSocket.Close()
msgToSend.strMessage = "<<<" & msgReceived.strName & " has left the room>>>"
Exit Select
Case Command.Message
'Set the text of the message that we will broadcast to all users
msgToSend.strMessage = msgReceived.strName & ": " & msgReceived.strMessage
Exit Select
Case Command.List
'Send the names of all users in the chat room to the new user
msgToSend.cmdCommand = Command.List
msgToSend.strName = Nothing
msgToSend.strMessage = Nothing
'Collect the names of the user in the chat room
For Each client As ClientInfo In clientList
'To keep things simple we use asterisk as the marker to separate the user names
msgToSend.strMessage += client.strName & "*"
Next
message = msgToSend.ToByte()
'Send the name of the users in the chat room
clientSocket.BeginSend(message, 0, message.Length, SocketFlags.None, New AsyncCallback(AddressOf OnSend), clientSocket)
Exit Select
End Select
If msgToSend.cmdCommand <> Command.List Then
'List messages are not broadcasted
message = msgToSend.ToByte()
For Each clientInfo As ClientInfo In clientList
If clientInfo.socket IsNot clientSocket OrElse msgToSend.cmdCommand <> Command.Login Then
'Send the message to all users
clientInfo.socket.BeginSend(message, 0, message.Length, SocketFlags.None, New AsyncCallback(AddressOf OnSend), clientInfo.socket)
End If
Next
Debug.Print(msgToSend.strMessage)
End If
'If the user is logging out then we need not listen from her
If msgReceived.cmdCommand <> Command.Logout Then
'Start listening to the message send by the user
clientSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None, New AsyncCallback(AddressOf OnReceive), clientSocket)
End If
Catch ex As Exception
MessageBox.Show(ex.Message, "SGSserverTCP", MessageBoxButtons.OK, MessageBoxIcon.[Error])
End Try
End Sub
Public Sub OnSend(ar As IAsyncResult)
Try
Dim client As Socket = DirectCast(ar.AsyncState, Socket)
client.EndSend(ar)
Catch ex As Exception
MessageBox.Show(ex.Message, "SGSserverTCP", MessageBoxButtons.OK, MessageBoxIcon.[Error])
End Try
End Sub
End Class
'The data structure by which the server and the client interact with
'each other
Class Data
Public strName As String
'Name by which the client logs into the room
Public strMessage As String
'Message text
Public cmdCommand As Command
'Command type (login, logout, send message, etcetera)
'Default constructor
Public Sub New()
cmdCommand = Command.Null
strMessage = Nothing
strName = Nothing
End Sub
'Converts the bytes into an object of type Data
Public Sub New(data__1 As Byte())
'The first four bytes are for the Command
cmdCommand = CType(BitConverter.ToInt32(data__1, 0), Command)
'The next four store the length of the name
Dim nameLen As Integer = BitConverter.ToInt32(data__1, 4)
'The next four store the length of the message
Dim msgLen As Integer = BitConverter.ToInt32(data__1, 8)
'This check makes sure that strName has been passed in the array of bytes
If nameLen > 0 Then
Me.strName = Encoding.UTF8.GetString(data__1, 12, nameLen)
Else
Me.strName = Nothing
End If
'This checks for a null message field
If msgLen > 0 Then
Me.strMessage = Encoding.UTF8.GetString(data__1, 12 + nameLen, msgLen)
Else
Me.strMessage = Nothing
End If
End Sub
'Converts the Data structure into an array of bytes
Public Function ToByte() As Byte()
Dim result As New List(Of Byte)()
'First four are for the Command
result.AddRange(BitConverter.GetBytes(CInt(cmdCommand)))
'Add the length of the name
If strName IsNot Nothing Then
result.AddRange(BitConverter.GetBytes(strName.Length))
Else
result.AddRange(BitConverter.GetBytes(0))
End If
'Length of the message
If strMessage IsNot Nothing Then
result.AddRange(BitConverter.GetBytes(strMessage.Length))
Else
result.AddRange(BitConverter.GetBytes(0))
End If
'Add the name
If strName IsNot Nothing Then
result.AddRange(Encoding.UTF8.GetBytes(strName))
End If
'And, lastly we add the message text to our array of bytes
If strMessage IsNot Nothing Then
result.AddRange(Encoding.UTF8.GetBytes(strMessage))
End If
Return result.ToArray()
End Function
End Class
Client source:
Enum Command
Login '0 Log into the server
Logout '1 Logout of the server
Message '2 Send a text message to all the chat clients
List '3 Get a list of users in the chat room from the server
Null '4 No command
End Enum
Public Class frmMain
Public clientSocket As System.Net.Sockets.Socket
'The main client socket
Public strName As String
'Name by which the user logs into the room
Private byteData As Byte() = New Byte(1023) {}
Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Call StartChat()
End Sub
Private Sub StartChat()
clientSocket = New System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp)
Dim ipAddress As System.Net.IPAddress = System.Net.IPAddress.Parse("127.0.0.1")
Dim ipEndPoint As New System.Net.IPEndPoint(ipAddress, 2277)
clientSocket.BeginConnect(ipEndPoint, New AsyncCallback(AddressOf OnLoginConnect), Nothing)
End Sub
Private Sub OnLoginConnect(ar As IAsyncResult)
clientSocket.EndConnect(ar)
'We are connected so we login into the server
Dim msgToSend As New Data()
msgToSend.cmdCommand = Command.Login
msgToSend.strName = txtUser.Text
msgToSend.strMessage = Nothing
Dim b As Byte() = msgToSend.ToByte()
'Send the message to the server
clientSocket.BeginSend(b, 0, b.Length, System.Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf OnLoginSend), Nothing)
End Sub
Private Sub OnLoginSend(ar As IAsyncResult)
clientSocket.EndSend(ar)
strName = txtUser.Text
Call LoggedIn()
End Sub
'***************
Private Sub LoggedIn()
'The user has logged into the system so we now request the server to send
'the names of all users who are in the chat room
Dim msgToSend As New Data()
msgToSend.cmdCommand = Command.List
msgToSend.strName = strName
msgToSend.strMessage = Nothing
byteData = msgToSend.ToByte()
clientSocket.BeginSend(byteData, 0, byteData.Length, System.Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf OnSend), Nothing)
byteData = New Byte(1023) {}
'Start listening to the data asynchronously
clientSocket.BeginReceive(byteData, 0, byteData.Length, System.Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf OnReceive), Nothing)
End Sub
'Broadcast the message typed by the user to everyone
Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click
'Fill the info for the message to be send
Dim msgToSend As New Data()
'.ToString("M/d/yyyy h:mm:ss tt")
msgToSend.strName = "[" & Date.Now.ToString("h:mm:ss tt") & "] <" & strName & ">"
msgToSend.strMessage = txtMessage.Text
msgToSend.cmdCommand = Command.Message
Dim byteData As Byte() = msgToSend.ToByte()
'Send it to the server
clientSocket.BeginSend(byteData, 0, byteData.Length, System.Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf OnSend), Nothing)
txtMessage.Text = Nothing
End Sub
Private Sub OnSend(ar As IAsyncResult)
clientSocket.EndSend(ar)
End Sub
Private Sub OnReceive(ar As IAsyncResult)
'clientSocket.EndReceive(ar)
Dim bytesReceived As Long = clientSocket.EndReceive(ar)
If (bytesReceived > 0) Then
Me.Invoke(New iThreadSafe(AddressOf iThreadSafeFinish), New Object() {byteData})
End If
byteData = New Byte(1023) {}
clientSocket.BeginReceive(byteData, 0, byteData.Length, System.Net.Sockets.SocketFlags.None, New AsyncCallback(AddressOf OnReceive), Nothing)
End Sub
Private Delegate Sub iThreadSafe(ByVal ibyteData As Byte())
Private Sub iThreadSafeFinish(ByVal ibyteData As Byte())
Dim msgReceived As New Data(ibyteData)
'Accordingly process the message received
Debug.Print(msgReceived.cmdCommand)
Select Case msgReceived.cmdCommand
Case 0 'login
lstChatters.Items.Add(msgReceived.strName)
Exit Select
Case 1 'log out
lstChatters.Items.Remove(msgReceived.strName)
Exit Select
Case 2 'msg
Exit Select
Case 3 'List
lstChatters.Items.AddRange(msgReceived.strMessage.Split("*"c))
lstChatters.Items.RemoveAt(lstChatters.Items.Count - 1)
txtChatBox.Text += "<<<" & strName & " has joined the room>>>" & vbCr & vbLf
Exit Select
End Select
If msgReceived.strMessage IsNot Nothing AndAlso msgReceived.cmdCommand <> Command.List Then
txtChatBox.Text += msgReceived.strMessage & vbCr & vbLf
End If
End Sub
Private Sub txtMessage_TextChanged(sender As Object, e As KeyEventArgs) Handles txtMessage.KeyDown
If e.KeyCode = Keys.Enter Then
btnSend_Click(sender, Nothing)
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
strName = txtUser.Text
End Sub
End Class
'The data structure by which the server and the client interact with
'each other
Class Data
Public strName As String
'Name by which the client logs into the room
Public strMessage As String
'Message text
Public cmdCommand As Command
'Command type (login, logout, send message, etcetera)
'Default constructor
Public Sub New()
cmdCommand = Command.Null
strMessage = Nothing
strName = Nothing
End Sub
'Converts the bytes into an object of type Data
Public Sub New(data__1 As Byte())
'The first four bytes are for the Command
cmdCommand = CType(BitConverter.ToInt32(data__1, 0), Command)
'The next four store the length of the name
Dim nameLen As Integer = BitConverter.ToInt32(data__1, 4)
'The next four store the length of the message
Dim msgLen As Integer = BitConverter.ToInt32(data__1, 8)
'This check makes sure that strName has been passed in the array of bytes
If nameLen > 0 Then
strName = System.Text.Encoding.UTF8.GetString(data__1, 12, nameLen)
Else
strName = Nothing
End If
'This checks for a null message field
If msgLen > 0 Then
strMessage = System.Text.Encoding.UTF8.GetString(data__1, 12 + nameLen, msgLen)
Else
strMessage = Nothing
End If
End Sub
'Converts the Data structure into an array of bytes
Public Function ToByte() As Byte()
Dim result As New List(Of Byte)()
'First four are for the Command
result.AddRange(BitConverter.GetBytes(CInt(cmdCommand)))
'Add the length of the name
If strName IsNot Nothing Then
result.AddRange(BitConverter.GetBytes(strName.Length))
Else
result.AddRange(BitConverter.GetBytes(0))
End If
'Length of the message
If strMessage IsNot Nothing Then
result.AddRange(BitConverter.GetBytes(strMessage.Length))
Else
result.AddRange(BitConverter.GetBytes(0))
End If
'Add the name
If strName IsNot Nothing Then
result.AddRange(System.Text.Encoding.UTF8.GetBytes(strName))
End If
'And, lastly we add the message text to our array of bytes
If strMessage IsNot Nothing Then
result.AddRange(System.Text.Encoding.UTF8.GetBytes(strMessage))
End If
Return result.ToArray()
End Function
End Class
You are assuming that you will receive an entire message at a time. TCP does not guarantee to preserve the chunks that you wrote. Use the return value bytesReceived to determine how many bytes are actually in the buffer.
Your code must be able to deal with the possibility of receiving all outstanding data one byte at a time.

vb.net server - multiple clients continually processing different information

I am looking for help with writing a server application to serve an updating text stream to clients. My requirements are as follows:
I need to be able to have a client request information on server port 7878 and receive back an initial set of values, the changed values would then be reported every 5 seconds. The hanging point for me has been connecting another client. I need to be able to connect a 2nd (or 3rd or 4th) client while the first is still running. The second client would receive the initial values and then begin updating as well. I need the two streams to be completely independent of each other. Is this possible with VB.Net and TCP sockets?
Edit to add: I have pasted some of my code below of what I can share. WriteLog is a separate sub that isn't really relevant to my problem. This code will allow for a client to connect and then allow for another client to connect, but all transmissions to the 1st client stop on a new connection.
Public Class ServerApp
Dim serverSocket As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Dim clientSocket As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Private Sub ServerApp_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
WriteLog(String.Format("Start of form load.."))
Dim listener As New Thread(New ThreadStart(AddressOf ListenForRequests))
listener.IsBackground = True
listener.Start()
End Sub
Private Sub ListenForRequests()
Dim CONNECT_QUEUE_LENGTH As Integer = 4
serverSocket.Bind(New IPEndPoint(IPAddress.Any, 7878))
serverSocket.Listen(CONNECT_QUEUE_LENGTH)
serverSocket.BeginAccept(New AsyncCallback(AddressOf OnAccept), Nothing)
End Sub
Private Sub OnAccept(ByVal ar As IAsyncResult)
clientSocket = serverSocket.EndAccept(ar)
serverSocket.BeginAccept(New AsyncCallback(AddressOf OnAccept), Nothing)
WriteLog("just accepted new client")
Try
clientSocket.Send(Encoding.UTF8.GetBytes("first response on connect"), SocketFlags.None)
While True
clientSocket.Send(Encoding.UTF8.GetBytes("string of updates"), SocketFlags.None)
Thread.Sleep(5000)
End While
Catch ex As Exception
WriteLog(ex.Message)
WriteLog("Remote host has disconnected")
End Try
End Sub
End Class
I recommend trying out the UdpClient class, i find it easier to use and understand.
Now for some code...
Imports System.Net.Sockets
Imports System.Threading
Imports System.Text
Imports System.Net
Public Class Form1
Private port As Integer = 7878
Private Const broadcastAddress As String = "255.255.255.255"
Private receivingClient As UdpClient
Private sendingClient As UdpClient
Private myTextStream As String = "Blah blah blah"
Private busy As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
InitializeSender()
InitializeReceiver()
End Sub
Private Sub InitializeSender()
Try
sendingClient = New UdpClient(broadcastAddress, port)
sendingClient.EnableBroadcast = True
Catch ex As Exception
MsgBox("Error, unable to setup sender client on port " & port & "." & vbNewLine & vbNewLine & ex.ToString)
End Try
End Sub
Private Sub InitializeReceiver()
Try
receivingClient = New UdpClient(port)
ThreadPool.QueueUserWorkItem(AddressOf Receiver)
Catch ex As Exception
MsgBox("Error, unable to setup receiver on port " & port & "." & vbNewLine & vbNewLine & ex.ToString)
End
End Try
End Sub
Private Sub sendStream()
busy = True
Dim i% = 0
Do While i < 4
If myTextStream <> "" Then
Dim data() As Byte = Encoding.ASCII.GetBytes(myTextStream)
Try
sendingClient.Send(data, data.Length)
Catch ex As Exception
MsgBox("Error, unable to send stream." & vbNewLine & vbNewLine & ex.ToString)
End
End Try
End If
Thread.Sleep(5000)
i += 1
Loop
busy = False
End Sub
Private Sub Receiver()
Dim endPoint As IPEndPoint = New IPEndPoint(IPAddress.Any, port)
While True
Dim data() As Byte
data = receivingClient.Receive(endPoint)
Dim incomingMessage As String = Encoding.ASCII.GetString(data)
If incomingMessage = "what ever the client is requesting, for example," & "GET_VALUES" Then
If busy = False Then Call sendStream()
End If
End While
End Sub
End Class
A few links that may help: Here,
here,
here and
here.