![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
Newbie
Join Date: Dec 2005
Posts: 2
Rep Power: 0
![]() |
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. |
|
|
|
|
|
#2 |
|
Troll
Join Date: Apr 2005
Location: Texas
Posts: 732
Rep Power: 4
![]() |
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 |
|
|
|
|
|
#3 |
|
Professional Programmer
|
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. |
|
|
|
|
|
#4 |
|
Newbie
Join Date: Dec 2005
Posts: 2
Rep Power: 0
![]() |
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. ![]() |
|
|
|
|
|
#5 |
|
Expert Programmer
|
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 |
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|