Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Dec 25th, 2005, 9:15 AM   #1
JohnK
Newbie
 
Join Date: Dec 2005
Posts: 2
Rep Power: 0 JohnK is on a distinguished road
Getting data from a socket when it arrives

I am currently in the process of writing a client that will connect to Battle.net and I was wondering if there was a way to catch data right when it arrives at the socket. The only way I can think of off the top of my head is to do something like this:
Dim BnetSock As Socket
Dim IncomingData(0) As Byte

...

Do While BnetSock.Connected
	If BnetSock.Available > 0 Then
		ReDim IncomingData(BnetSock.Available - 1)
		BnetSock.Receive(IncomingData)
	End If
Loop

But as you might imagine, that's really CPU intensive.

If anyone has any ideas on how I might be able to instantaneously retrieve incoming data (I'm really hoping there is some kind of event that gets raised) please offer some assistance.
JohnK is offline   Reply With Quote
Old Dec 25th, 2005, 5:16 PM   #2
Dameon
Troll
 
Dameon's Avatar
 
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4 Dameon is on a distinguished road
It depends on the protocol. There is typically a field for the number of bytes to follow in the next chunk rather than an ending sentinel. In that case, I would use the more common overload of Receive (the one that takes a start position in the buffer, the bytes to read, etc) to make sure you get all the header bytes, allocate a new array of the specified size, and again use the more common overload of Receive to get all the bytes.

Here's an excerpt from the Element source code (Net.cs, BaseTcpConnection.ReceiveMessage). It uses TcpClient instead of Socket, but you get the idea.

byte[] recvBuff = new byte[4];
int read = 0;
int totalRead = 0;
int length = 0;

while (totalRead < 4) 
{
	   read = sock.GetStream().Read(recvBuff, totalRead, recvBuff.Length - totalRead);
	   if (read == 0) throw new Exception("Remote closed the connection");
					totalRead += read;
}

length = BitConverter.ToInt32(recvBuff,0);

if (length > MAX_SIZE) throw new Exception("Message reported too large");

totalRead = 0;
read = 0;
recvBuff = new byte[length];
					
while (totalRead < length) 
{
	 read = sock.GetStream().Read(recvBuff, totalRead, recvBuff.Length - totalRead);
	 if (read == 0) throw new Exception("Remote closed the connection");
	 totalRead += read;
}

As for being cpu intensive, add a delay between each poll. If the code to read the socket is in its own thread, use Sleep for perhaps 0 or 1 milliseconds (0 yields to waiting threads without specifically setting an interval to wait). If not, you can get away with a timer.
__________________
MD5(sig) = bcef75433db02e9ad9bf81d6f7c5c270
Dameon is offline   Reply With Quote
Old Dec 25th, 2005, 7:30 PM   #3
andro
Professional Programmer
 
Join Date: Oct 2005
Location: California
Posts: 310
Rep Power: 3 andro is on a distinguished road
Send a message via AIM to andro
This thread/forum might be useful to you now and during your bots development. Check it out, follow the links.

http://forum.valhallalegends.com/index.php?topic=7220.0

Last edited by andro; Dec 25th, 2005 at 7:50 PM.
andro is offline   Reply With Quote
Old Dec 25th, 2005, 8:07 PM   #4
JohnK
Newbie
 
Join Date: Dec 2005
Posts: 2
Rep Power: 0 JohnK is on a distinguished road
The main issue I am having is that I don't know when to expect the data. It's a chat client, somewhat similar to IRC (you enter a room and there may be as many as 40 users typing at once) which is why I need to get all the data from the socket as it arrives.

Is the best thing to do still just add a wait for each poll? That is a waste of a lot of CPU cycles.

EDIT: It's not a bot, it's going to be a plugin for Trillian.
JohnK is offline   Reply With Quote
Old Jan 3rd, 2006, 4:46 PM   #5
Rory
Expert Programmer
 
Rory's Avatar
 
Join Date: Jan 2005
Location: London
Posts: 542
Rep Power: 4 Rory is on a distinguished road
Send a message via MSN to Rory
You could also use the non-blocking call Socket.BeginReceive which takes a pointer to a buffer and invokes a callback on a spawned temporary thread. However be sure to make use of the provided space for a state object for example if more data arrives while the callback is still running - it's there for a reason but you'll need to implement its workings yourself.

In fact here's a wrapper I had kicking around which is somewhat reminiscent of the old Winsock control. It's from a heavily multithreaded FTP server so multiple instances may not be happy together. Also the buffer size is a bit random too.

Imports System
Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Public Class clsStateObject
    Public workSocket As Socket = Nothing
    Public BufferSize As Integer = 1024
    Public buffer(1024) As Byte
    Public sb As New StringBuilder
End Class

Public Class SocketsClient
    Public Event onConnect()
    Public Event onError(ByVal Description As String)
    Public Event onDataArrival(ByVal Data As Byte(), ByVal TotalBytes As Integer)
    Public Event onDisconnect()
    Public Event onSendComplete(ByVal DataSize As Integer)

    Private Shared response As [String] = [String].Empty
    Private Shared port As Integer = 21
    Private Shared ipHostInfo As IPHostEntry
    Private Shared ipAddress As ipAddress
    Private Shared client As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
    Public Sub Connect(ByVal RemoteHostName As String, ByVal RemotePort As Integer)
        Try
            client = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            port = RemotePort
            ipHostInfo = Dns.Resolve(RemoteHostName)
            ipAddress = ipHostInfo.AddressList(0)
            Dim remoteEP As New IPEndPoint(ipAddress, port)
            client.BeginConnect(remoteEP, AddressOf sockConnected, client)
        Catch
            RaiseEvent onError(Err.Description)
            Exit Sub
        End Try
    End Sub
    Public Sub SendData(ByVal Data() As Byte)
        Try
            Dim byteData As Byte() = Data
            client.BeginSend(byteData, 0, byteData.Length, 0, AddressOf sockSendEnd, client)
        Catch
            RaiseEvent onError(Err.Description)
            Exit Sub
        End Try
    End Sub
    Public Sub Disconnect()
        Try
            client.Shutdown(SocketShutdown.Both)
        Catch
        End Try
        client.Close()
    End Sub
    Public Function StringToBytes(ByVal Data As String) As Byte()
        StringToBytes = ASCIIEncoding.ASCII.GetBytes(Data)
    End Function
    Public Function BytestoString(ByVal Data As Byte()) As String
        BytestoString = ASCIIEncoding.ASCII.GetString(Data)
    End Function
    Private Sub sockConnected(ByVal ar As IAsyncResult)
        Try
            If client.Connected = False Then
                RaiseEvent onError("Connection refused.")
                Return
            End If
            Dim state As New clsStateObject
            state.workSocket = client
            client.BeginReceive(state.buffer, 0, state.BufferSize, 0, AddressOf sockDataArrival, state)
            RaiseEvent onConnect()
        Catch
            RaiseEvent onError(Err.Description)
            Exit Sub
        End Try
    End Sub
    Private Sub sockDataArrival(ByVal ar As IAsyncResult)
        Dim state As clsStateObject = CType(ar.AsyncState, clsStateObject)
        Dim client As Socket = state.workSocket
        Dim bytesRead As Integer

        Try
            bytesRead = client.EndReceive(ar)
        Catch
            Exit Sub
        End Try

        Try
            Dim Data() As Byte = state.buffer
            If bytesRead = 0 Then
                client.Shutdown(SocketShutdown.Both)
                client.Close()
                RaiseEvent onDisconnect()
                Exit Sub
            End If
            ReDim state.buffer(1024)

            client.BeginReceive(state.buffer, 0, state.BufferSize, SocketFlags.None, AddressOf sockDataArrival, state)
            RaiseEvent onDataArrival(Data, bytesRead)
        Catch
            RaiseEvent onError(Err.Description)
            Exit Sub
        End Try
    End Sub
    Private Sub sockSendEnd(ByVal ar As IAsyncResult)
        Try
            Dim client As Socket = CType(ar.AsyncState, Socket)
            Dim bytesSent As Integer = client.EndSend(ar)
            RaiseEvent onSendComplete(bytesSent)
        Catch
            RaiseEvent onError(Err.Description)
            Exit Sub
        End Try
    End Sub
    Public ReadOnly Property Connected() As Boolean
        Get
            Try
                Return client.Connected
            Catch
                RaiseEvent onError(Err.Description)
                Exit Property
            End Try
        End Get
    End Property
End Class
Rory is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 4:28 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC