' A "filtered" Chat Server by William Yu
' Clients can connect to this server, and talk to each other, but
' what gets filtered out are all those nasty swear words!
' If you've never programmed with sockets before, don't worry, this is
' as easy as it gets... sort of.
' Brief Overview of QSocket (Server-side)
' -- It is an interface only
'
' Transferred AS INTEGER
' -- Poll this value after any Read/Write
' -- Returns number of bytes sent/received
' Open(PortNum AS INTEGER)
' -- Used only by server, opens a TCP/IP connection
' -- Returns a socket file descriptor, store it!
' Accept(MasterSocket AS INTEGER)
' -- This function will block until a connection is established
' -- Returns a client socket file descriptor
' ConnectionReady(MasterSocket AS INTEGER)
' -- This function just checks if a connection is pending
' -- If so, then you should Accept the connection.
' -- This is a non-blocking function call, returns 0 if no connection is ready.
' IsClientReady(MasterSocket AS INTEGER, ClientSocket AS INTEGER)
' -- Non-blocking function call that determines if a client has sent
' some data to the "MasterSocket"
' ReadLine(ClientSocket AS INTEGER) AS STRING
' -- Read a complete line (ended with a linefeed character)
' -- Returns the line read, from the ClientSocket.
' -- Always check Socket.Transferred after this read operation.
' -- If Transferred = -1 then an error has occurred (client disconnected).
' WriteLine(ClientSocket AS INTEGER, Message AS STRING) AS INTEGER
' -- Function call that sends a message over to the client.
' -- Returns LEN(Message) on success, -1 on failure
' ReadBytes/WriteBytes will be implemented later, I just felt like
' releasing this as is (because of some memory leaks).
$APPTYPE GUI
$TYPECHECK ON '' I get in so many trouble with it OFF!!!
CONST DELAY = 500 ' In Milliseconds
CONST PortNum = 5000 ' Anything above 1024 should work, with some exceptions
CONST False = 0
CONST True = NOT False
DECLARE SUB TimerExpired
DIM Socket AS QSocket
CREATE Form AS QForm
Height = 300
Width = 400
Caption = "Chat Server"
Center
CREATE ListBox AS QListBox
Top = 100
Height = 200
Width = Form.ClientWidth
AddItems "Chat server by William Yu", STRING$(30,"-")
ItemIndex = 0
END CREATE
CREATE Label1 AS QLabel
Top = 10
Left = 10
Height = 14
Caption = "Server: " + Socket.GetHostName
END CREATE
CREATE Label2 AS QLabel
Top = 29
Left = 10
Height = 14
Caption = "Port: "+STR$(PortNum)
END CREATE
CREATE Label3 AS QLabel
Top = 48
Left = 10
Height = 14
Caption = "Connections: 0"
END CREATE
END CREATE
DIM Timer1 AS QTimer
DIM MasterSocket AS INTEGER, NumClients AS INTEGER
DIM Client(1 TO 50) AS INTEGER
DIM SwearCount AS INTEGER
DIM Swears(1 TO 50) AS STRING
Swears(1) = "ASS"
Swears(2) = "BASTARD"
Swears(3) = "HELL" ' Yeah, I'm skipping the most obvious :)
' Add yours here, I'm keeping this code "clean"
SwearCount = 3
Timer1.Interval = DELAY ' Some Delay between checks
' Don't want to become a CPU hog
Timer1.OnTimer = TimerExpired
NumClients = 0
MasterSocket = Socket.Open(PortNum)
IF MasterSocket = -1 THEN
PRINT "Server error: could not make connection, maybe try another port..."
END
END IF
Form.ShowModal
SUB SwapClients(N AS INTEGER)
'-- Get rid of disconnected client
DIM I AS INTEGER
FOR I = N to NumClients
Client(I) = Client(I+1)
NEXT
END SUB
FUNCTION Filter(Message AS STRING) AS STRING
DIM I AS INTEGER, N AS INTEGER
' Very very simple, you should write your own filter, as this is just a
' demonstration, GRASS and ASS will both be processed!
FOR I = 1 To SwearCount
'' Dang, seems to seg fault sometimes...
N = INSTR(UCASE$(Message), Swears(I))
WHILE N
Message = LEFT$(Message, N-1) + STRING$(LEN(Swears(I)), "*") + _
MID$(Message, N+LEN(Swears(I)), LEN(Message))
N = INSTR(UCASE$(Message), Swears(I))
WEND
NEXT I
Filter = Message
END FUNCTION
SUB SendToClients (Message AS STRING)
DIM I AS INTEGER, N AS INTEGER
FOR I = 1 to NumClients
N = Socket.WriteLine(Client(I), Message)
NEXT
END SUB
SUB TimerExpired
DIM I AS INTEGER
DIM S AS STRING
Timer1.Enabled = False
Timer1.Interval = DELAY ' Restore delay
IF Socket.ConnectionReady(MasterSocket) > 0 THEN
NumClients = NumClients + 1
Client(NumClients) = Socket.Accept(MasterSocket)
ListBox.AddItems "Client "+STR$(NumClients)+": Connected to server."
IF Socket.WriteLine(Client(NumClients), "Welcome to William's Chat Server!") = -1 THEN
ListBox.AddItems "Client "+STR$(NumClients)+": Disconnected from server."
NumClients = NumClients - 1
ELSE
Label3.Caption = "Connections: "+STR$(NumClients)
END IF
END IF
FOR I = 1 to NumClients '' Check for ready clients
IF Socket.IsClientReady(MasterSocket, Client(I)) > 0 THEN
S = Socket.ReadLine(Client(I))
ListBox.AddItems "Client "+STR$(I)+": "+S
IF Socket.Transferred = -1 THEN
ListBox.DelItems ListBox.ItemCount-1
ListBox.AddItems "Client "+STR$(I)+": Disconnected from server."
NumClients = NumClients - 1
Label3.Caption = "Connections: "+STR$(NumClients)
SwapClients(I)
SendToClients("Client "+STR$(I)+": Disconnected.")
ELSE
S = Filter(S)
SendToClients("Client "+STR$(I)+": "+S)
END IF
END IF
NEXT
Timer1.Enabled = True
END SUB