Code archives/Networking/Simple TCP/IP Network Library

This code has been declared by its author to be Public Domain code.

Download source code

Simple TCP/IP Network Library by Matty2009
I wanted to make my next multiplayer game use TCP/IP rather than UDP as I wanted reliable messaging and am not making a fast paced action game. So this is what I put together.

The code has 1 global variable (MyTCPServer) that gets set upon running InitialiseNetwork(Port,TimeoutInSeconds) and a type called "ConnectionObject" which stores the IP address of any machine which connects to the PC using this network.

If a client leaves the network/exits the game etc the command "CloseNetwork()" must be called on the client machine to ensure the server knows not to keep trying to establish connections with the client (which can cause it to hang for a few seconds).

the other commands in this library are:

SendMessage(MessageType,Message$,IP$,Port)
- Message Type is a value from 0-255 with a small number of reserved values 100-103 relating to 'connection/join/exiting'. Message$ is basically a string that you wish to send. IP$ is the dottedip format or webaddress you wish to send the message to - usually you would use the values stored in the connection object, and port should be left as it is (2009) unless there is a conflict.

UpdateNetwork() - call this regularly to check for messages, ideally every frame. I have left this fairly open so that you can handle your strings received from the network as you wish.

The various "Pack"/"UnPack" commands are present to convert bytes/shorts/ints/floats to a string ready to send in the appropriate amount of space (1 byte/2bytes/4bytes/4bytes respectively).

The simple example uses a hard coded IP address - you will need to change this, and also assumes the commandline parameter (set within the blitz ide) is set to "server" for the server/host. All the example does is update the position of a rectangle/dot on the screen with some simple smoothing applied.

Some of the functions have a return value which indicates whether they were successfully complete or not.
;simple example
Graphics 800,600,0,2
InitialiseNetwork(2009,3)
Global screenx#,screeny#,dx#,dy#	;for little block that we will move about the screen on the client's 

machine.

If CommandLine$()="server" Then ;this all assumes the server code is run first.
	;do nothing
Else
	While SendMessage(100,"","192.168.1.6") :Wend ;you would have to change these lines yourself normally
EndIf 
SetBuffer BackBuffer()
Repeat 
UpdateNetwork()
If CommandLine$()="server" Then 
screenx=400.0+300.0*(Sin(MilliSecs()/50))*(Cos(MilliSecs()/250))
screeny=300.0+(100.0*Sin(MilliSecs()/30))+(150.0*Cos(MilliSecs()/125))
Else

screenx=screenx+dx
screeny=screeny+dy

EndIf 

If MilliSecs()>time And CommandLine$()="server" Then 
	time=MilliSecs()+100
	For Connection.ConnectionObject=Each ConnectionObject
		SendMessage(1,PackFloat(screenx)+PackFloat(screeny),Connection\IP)
	Next
EndIf 



Cls
Rect screenx,screeny,4,4,1
Flip
Until KeyDown(1)


CloseNetwork()
End



;Basic TCP Library below

Global MyTCPServer  ;server handle for the local machine's TCP Server. Always non zero and refers to local machine

Type ErrorObject

Field Message$

End Type 

Type ConnectionObject

Field IP$

End Type 

Function InitialiseNetwork(Port=2009,TimeoutinSeconds=10)

MyTCPServer = CreateTCPServer(Port)
If MyTCPServer = 0 Then 
	Error.ErrorObject=New ErrorObject
	Error\Message = "Unable to CreateTCPServer listening on port:"+Port
	Return 1 ;error 
EndIf 
TCPTimeouts TimeOutInSeconds*1000,0
Return 0	;no error
End Function 
Function CloseNetwork()
For Connection.ConnectionObject=Each ConnectionObject
	SendMessage(102,"",Connection\IP) ;Let others know we've quit.
Next
If MyTCPServer <> 0 Then 
	CloseTCPServer MyTCPServer	
EndIf 

End Function 

Function SendMessage(MessageType,Message$,IP$,Port=2009)

stream=OpenTCPStream(IP$,Port)
If stream = 0 Then 
	Error.ErrorObject=New ErrorObject
	Error\Message= "Unable to open TCPStream to IP:"+IP+" and on port:"+port
	

	For Connection.ConnectionObject=Each ConnectionObject
		If Connection\IP=IP Then Delete Connection 
	Next
	Return 1	;Error condition
EndIf
	WriteString stream,PackByte(MessageType)+Message
	CloseTCPStream stream
	
	Return 0	;Successful
End Function 

Function UpdateNetwork()
;Message Types - 
;100 - Request received by server for client to join
;101 - Acknowledgement received by client to say that server has accepted them
;102 - Server receives message saying client has left 
;103 - Client(s) receive message from server letting them know other client's machine on network's IP Address.
If MyTCPServer<>0 Then 

	stream=AcceptTCPStream(MyTCPServer)
	If stream<>0 Then 
		streamstring$=ReadString(stream)
		
		If Len(streamstring)>0 Then 
		Select UnPackByte(Left(streamstring,1))
		
			Case 100	;Remote machine has requested to join the network
				IsNewConnection=True	;Check if stream is from a newly connected user
				For Connection.ConnectionObject=Each ConnectionObject
					If Connection\IP$ = DottedIP(TCPStreamIP(stream)) Then 

IsNewConnection=False:Exit 
				Next		
				
				;Create a new connection object if it is a new user
				If IsNewConnection=True Then 
					Connection.ConnectionObject=New ConnectionObject
					Connection\IP$=DottedIP(TCPStreamIP(stream))	
					SendMessage(101,"",Connection\IP)	
				EndIf 
				;Server needs to update every other client with the new client's details.
				For Connection.ConnectionObject=Each ConnectionObject
					If Connection\IP$<>DottedIP(TCPStreamIP(stream)) Then 
						SendMessage(103,PackInt(TCPStreamIP(stream)),Connection\IP)
					EndIf 
				Next				
			Case 101  ;Local machine has received a 'yes you can join message' from the server
				IsNewConnection=True	;Create server connection object
				For Connection.ConnectionObject=Each ConnectionObject
					If Connection\IP$ = DottedIP(TCPStreamIP(stream)) Then 

IsNewConnection=False:Exit 
				Next		
				
				;Create a new connection object.
				If IsNewConnection=True Then 
					Connection.ConnectionObject=New ConnectionObject
					Connection\IP$=DottedIP(TCPStreamIP(stream))	
				EndIf 
			Case 102 ;Remote machine has let us know it is leaving....used to prevent any 'hangs' that 

can occur if a user is not connected
				For Connection.ConnectionObject=Each ConnectionObject
					If Connection\IP$ = DottedIP(TCPStreamIP(stream)) Then Delete Connection
				Next		
			Case 103
				IsNewConnection=True	
				For Connection.ConnectionObject=Each ConnectionObject
					If Connection\IP$ = DottedIP(UnPackInt(Mid(streamstring,2,4))) Then 

IsNewConnection=False:Exit 
				Next		
				;Create a new connection object.
				If IsNewConnection=True Then 
					Connection.ConnectionObject=New ConnectionObject
					Connection\IP$=DottedIP(UnPackInt(Mid(streamstring,2,4)))
				EndIf 

			
			;User defined message types now.
			Case 1	;this is for the simple example above, you will most likely want to remove it
				scrx#=UnPackFloat(Mid(streamstring,2,4))		
				scry#=UnPackFloat(Mid(streamstring,6,4))
				dx#=(scrx-screenx)/6.0
				dy#=(scry-screeny)/6.0
			End Select 
		EndIf 
		
		CloseTCPStream stream
	Else
	
		;no packets received, so don't do anything.
	EndIf 
Else

	Return 1	;Error condition - in this case the MyTCPServer value is zero.

EndIf
Return 0	;Successful
End Function 


Function PackInt$(value)
;Pack a 4 byte integer value into a 4 byte string

A=(value Shr 24) And 255
B=(value Shr 16) And 255
C=(value Shr 8) And 255
D=value And 255
Return Chr$(A)+Chr$(B)+Chr$(C)+Chr$(D)

End Function 
Function PackShort$(value)
;Pack a 2 byte short value into a 2 byte string

A=(Value Shr 8) And 255
B=Value And 255
Return Chr$(A)+Chr$(B)

End Function 
Function PackByte$(value)
;Pack a 1 byte value into a 1 byte string

Return Chr$(Value And 255)

End Function 
Function PackFloat$(value#)
;Pack a 4 byte float into a 4 byte string

Bank=CreateBank(4)
PokeFloat Bank,0,value
A=PeekByte (Bank,0)
B=PeekByte (Bank,1)
C=PeekByte (Bank,2)
D=PeekByte (Bank,3)
FreeBank Bank
Return Chr$(A)+Chr$(B)+Chr$(C)+Chr$(D)

End Function 
Function UnPackInt(value$)
;Unpack a 4 byte string into a 4 byte integer

Return (Asc(Mid(value,1,1)) Shl 24) Or (Asc(Mid(value,2,1)) Shl 16) Or (Asc(Mid(value,3,1)) Shl 8) Or (Asc(Mid

(value,4,1)) )

End Function
Function UnPackShort(value$)
;Unpack a 2 byte string into a 2 byte short

Return (Asc(Mid(Value,1,1)) Shl 8) Or (Asc(Mid(Value,2,1)) )

End Function 
Function UnPackByte(value$)
;Unpack a 1 byte string into a 1 byte value
Return Asc(Value)
End Function 
Function UnPackFloat#(value$)
;Unpack a 4 byte string into a 4 byte float

Bank=CreateBank(4)
PokeByte Bank,0,Asc(Mid(value,1,1))
PokeByte Bank,1,Asc(Mid(value,2,1))
PokeByte Bank,2,Asc(Mid(value,3,1))
PokeByte Bank,3,Asc(Mid(value,4,1))
fvalue#=PeekFloat(bank,0)

FreeBank bank 
Return fvalue
End Function

Comments

_332009
Thanks :)


Wings2009
one thougt

If stream<>0 Then
streamstring$=ReadString(stream)

at 2004 i discover this is bad idea for a game :)


Matty2009
why? - if you are only writing strings to the stream it shouldn't be a problem.


Code Archives Forum