Suite du projet: Beaucoup de choses encore à faire. Le prototype fonctionne, le réseau aussi. Il reste à faire l'alternance des couleurs pour la lisibilité et une icône d'état pour les clients (Permet de l'utiliser comme systeme ICQ) pour les anciens qui connaissent

Le système utilise quelques
commandes IRC standard. Côté serveur, il suffit de vérifier si le premier caractère est un / alors c'est une commande. Sinon le serveur retourne le texte à tous les clients connectés. (Merci Blendman pour l'idée de la structure et de la boucle ForEach). J'ai aussi du ajouter un timer d'une seconde pour forcer le refresh des clients.
Le serveur aura aussi une version console pour tourner sur les machines linux sans interface graphique. (Je remplace la fenêtre par un fichier de log qu'ont peut afficher en scrolling avec tail -f)
Il reste encore des plantages à cause des tampons mémoire à vider après transmission et autres si une connexion n'est plus active)
Reste aussi à remplacer le ListViewGadget par un ListIconGadget pour changer les couleurs des lignes en fonction des utilisateurs et pemettre de mettre des colonnes et icônes)
Si vous voulez tester/améliorer
Pour simplifier, j'ai inclu le .pbf dans le source. À vous de reséparer si vous souhaitez utiliser le Form Designer
Le serveur.
À lancer sur un seul poste.
Code : Tout sélectionner
; PB IRC Server
; Marc56 10/11/21
; Modifications blendman
; todo: fermer tous les clients en quittant
OnErrorGoto(?Global_Error)
; XIncludeFile "PB_IRC_Server.pbf"
; Form Designer for Purebasic - 6.00
; Warning: this file uses a strict syntax, if you edit it, make sure to respect the Form Designer limitation or it won't be opened again.
;
; This code is automatically generated by the FormDesigner.
; Manual modification is possible to adjust existing commands, but anything else will be dropped when the code is compiled.
; Event procedures needs to be put in another source file.
;
Enumeration FormWindow
#Win_Server
EndEnumeration
Enumeration FormGadget
#Log
EndEnumeration
Enumeration FormFont
#Font_Win_Server_0
EndEnumeration
LoadFont(#Font_Win_Server_0,"Consolas", 10)
Declare ResizeGadgetsWin_Server()
Procedure OpenWin_Server(x = 10, y = 10, width = 300, height = 500)
OpenWindow(#Win_Server, x, y, width, height, "PB IRCd", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget)
CreateStatusBar(0, WindowID(#Win_Server))
AddStatusBarField(300)
StatusBarText(0, 0, "PB IRCd - v0.1")
ListViewGadget(#Log, 5, 5, 290, 465)
SetGadgetColor(#Log, #PB_Gadget_FrontColor,RGB(128,128,128))
SetGadgetColor(#Log, #PB_Gadget_BackColor,RGB(255,255,255))
SetGadgetFont(#Log, FontID(#Font_Win_Server_0))
EndProcedure
Procedure ResizeGadgetsWin_Server()
Protected FormWindowWidth, FormWindowHeight
FormWindowWidth = WindowWidth(#Win_Server)
FormWindowHeight = WindowHeight(#Win_Server)
ResizeGadget(#Log, 5, 5, FormWindowWidth - 10, FormWindowHeight - StatusBarHeight(0) - 12)
EndProcedure
; ------------------------------------------------- / form
Structure sClient
ID.i
Name$
Connected.i
EndStructure
Global NewList Client.sClient()
Global Receive_Txt$
Procedure Monitor(Message$)
; Display on server
AddGadgetItem(#Log, -1, Message$)
; todo: Log file
EndProcedure
Procedure Send_All(Message$)
If Message$ <> "" : Receive_Txt$ = Message$ : EndIf
ForEach Client()
If Client()\Connected = 1
SendNetworkString(Client()\ID, Receive_Txt$, #PB_UTF8)
EndIf
Next
EndProcedure
OpenWin_Server()
Monitor("Starting IRC Server...")
If InitNetwork() = 0
Monitor("Error - Can't initialize the network !")
Else
Monitor("Network Ready")
EndIf
If CountProgramParameters()
Port = Val(ProgramParameter(0))
Else
Port = 6832
EndIf
*Buffer = AllocateMemory(1000)
If Not CreateNetworkServer(0, Port)
Monitor("Error - Can't create the server (port in use ?).")
EndIf
Monitor("Listening on port: " + Str(Port))
SetWindowTitle(#Win_Server, "PB IRCd - Running...")
Repeat
Win_Event = WaitWindowEvent(1)
Net_Event = NetworkServerEvent()
Select Win_Event
Case #PB_Event_CloseWindow
Monitor("Close connection(s)")
CloseNetworkServer(0)
End
Case #PB_Event_SizeWindow
ResizeGadgetsWin_Server()
EndSelect
ID_Client = EventClient()
If NickName$ = "" :
NickName$ = LSet(Str(ID_Client), 8, " ")
EndIf
Select Net_Event
Case #PB_NetworkEvent_Connect
Monitor(NickName$ + " : Connected.")
AddElement(Client())
With Client()
\ID = ID_Client
\Name$ = NickName$
\Connected = 1
EndWith
Case #PB_NetworkEvent_Data
FillMemory(*Buffer, 1000)
Nb_Octets = ReceiveNetworkData(ID_Client, *Buffer, 1000)
Receive_Txt$ = PeekS(*Buffer, Nb_Octets, #PB_UTF8)
If Left(Receive_Txt$, 5) = "/nick"
NickName$ = Mid(Receive_Txt$, 6, 10)
Client()\Name$ = NickName$
Else
Monitor(Receive_Txt$)
EndIf
Send_All("")
Case #PB_NetworkEvent_Disconnect
Monitor(LTrim(NickName$) + " : has quit.")
Client()\Connected = 0
EndSelect
Until Quit = 1
Monitor("Server - Close connection")
CloseNetworkServer(0)
End
Global_Error:
MessageRequester("Error", ErrorMessage(), 16)
Le client
On peut en lancer plusieurs sur le même poste pour tester.
Il génère au démarrage un nom aléatoire (ID_ 0 à 99) on en change ensuite.
Sur un réseau local, mettre ladresse du serveur (donc pas 127.0.0.1)
Sur Internet, il faut translater le port au niveau de la box vers la machine qui héberge le serveur
(attention: il n'y a aucune sécurité pour l'instant)
Code : Tout sélectionner
; PB IRC Client
; Marc56 10/11/21
; Modifications blendman
OnErrorGoto(?Global_Error)
; XIncludeFile "PB_IRC.pbf"
; Form Designer for Purebasic - 6.00
; Warning: this file uses a strict syntax, if you edit it, make sure to respect the Form Designer limitation or it won't be opened again.
;
; This code is automatically generated by the FormDesigner.
; Manual modification is possible to adjust existing commands, but anything else will be dropped when the code is compiled.
; Event procedures needs to be put in another source file.
;
Enumeration FormWindow
#Win
EndEnumeration
Enumeration FormGadget
#Dialog
#Txt
#Btn_Send
#Btn_Connect
#Btn_Quit
#Str_Name
#Btn_Name
#Str_IP
#Str_Port
#Txt_Alias
EndEnumeration
Enumeration FormFont
#Font_Win_0
EndEnumeration
LoadFont(#Font_Win_0,"Consolas", 10)
Declare ResizeGadgetsWin()
Procedure OpenWin(x = 0, y = 0, width = 300, height = 500)
OpenWindow(#Win, x, y, width, height, "PB IRC", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
CreateStatusBar(0, WindowID(#Win))
AddStatusBarField(300)
StatusBarText(0, 0, "Ready.")
ListViewGadget(#Dialog, 5, 65, 290, 380)
SetGadgetFont(#Dialog, FontID(#Font_Win_0))
StringGadget(#Txt, 5, 447, 215, 25, "")
SetGadgetFont(#Txt, FontID(#Font_Win_0))
ButtonGadget(#Btn_Send, 225, 447, 70, 25, "Send", #PB_Button_Default)
SetGadgetFont(#Btn_Send, FontID(#Font_Win_0))
DisableGadget(#Btn_Send, 1)
ButtonGadget(#Btn_Connect, 155, 5, 75, 25, "Connect")
ButtonGadget(#Btn_Quit, 235, 5, 60, 25, "Quit")
StringGadget(#Str_Name, 55, 35, 95, 25, "")
SetGadgetFont(#Str_Name, FontID(#Font_Win_0))
ButtonGadget(#Btn_Name, 155, 35, 75, 25, "Change ")
StringGadget(#Str_IP, 5, 5, 95, 25, "127.0.0.1")
SetGadgetFont(#Str_IP, FontID(#Font_Win_0))
StringGadget(#Str_Port, 105, 5, 40, 25, "6832", #PB_String_Numeric)
SetGadgetFont(#Str_Port, FontID(#Font_Win_0))
TextGadget(#Txt_Alias, 10, 40, 40, 20, "Alias")
SetGadgetFont(#Txt_Alias, FontID(#Font_Win_0))
DisableGadget(#Txt, 1)
EndProcedure
Procedure ResizeGadgetsWin()
Protected FormWindowWidth, FormWindowHeight
FormWindowWidth = WindowWidth(#Win)
FormWindowHeight = WindowHeight(#Win)
ResizeGadget(#Dialog, 5, 65, FormWindowWidth - 10, FormWindowHeight - StatusBarHeight(0) - 97)
ResizeGadget(#Txt, 5, FormWindowHeight - 53, FormWindowWidth - 85, 25)
ResizeGadget(#Btn_Send, FormWindowWidth - 75, FormWindowHeight - 53, 70, 25)
ResizeGadget(#Btn_Connect, FormWindowWidth - 145, 5, 75, 25)
ResizeGadget(#Btn_Quit, FormWindowWidth - 65, 5, 60, 25)
ResizeGadget(#Btn_Name, 155, FormWindowHeight - 465, FormWindowWidth - 225, 25)
EndProcedure
; ------------------------- /form
Enumeration FormWindow
#Refresh
EndEnumeration
OpenWin()
AddKeyboardShortcut(#Win, #PB_Shortcut_Return, 13)
AddWindowTimer(#Win, #Refresh, 1000)
Global Name$ = LSet("ID_" + Str(Random(99, 1)), 8, " ")
SetGadgetText(#Str_Name, Name$)
If InitNetwork() = 0
AddGadgetItem(#Dialog, -1, "Error - Can't initialize the network !")
Else
AddGadgetItem(#Dialog, -1, "Network Ready.")
AddGadgetItem(#Dialog, -1, "Click [Connect]")
EndIf
Global IP$ = "127.0.0.1"
Global Port = 6832
Global ID_Cnx
Global *Buffer = AllocateMemory(1000)
Declare Disconnect()
Procedure Connect()
IP$ = GetGadgetText(#Str_IP)
Port = Val(GetGadgetText(#Str_Port))
; Bug: si pas de serveur actif
If GetGadgetText(#Btn_Connect) = "Disconnect"
Disconnect()
ProcedureReturn
EndIf
ID_Cnx = OpenNetworkConnection(IP$, Port)
If ID_Cnx
AddGadgetItem(#Dialog, -1, "Connect to " + IP$)
AddGadgetItem(#Dialog, -1, "ID connection " + Str(ID_Cnx))
StatusBarText(0, 0, "Connected to " + IP$ + ":" + Port)
StatusBarText(0, 0, "Port: " + Str(Port))
DisableGadget(#Txt, 0)
DisableGadget(#Btn_Send, 0)
SetGadgetText(#Btn_Connect, "Disconnect")
Name$ = RSet(GetGadgetText(#Str_Name), 8, " ")
SendNetworkString(ID_Cnx, "/New " + Name$, #PB_UTF8)
Else
AddGadgetItem(#Dialog, -1, "Can't connect to Server")
MessageRequester("Error", "Can't connect to Server", #PB_MessageRequester_Error) : End
EndIf
EndProcedure
Procedure Disconnect()
AddGadgetItem(#Dialog, -1, "End of Connection")
;CloseNetworkServer(0)
If ID_Cnx = 0 : ProcedureReturn : EndIf
SendNetworkString(ID_Cnx, "EOT " + Name$, #PB_UTF8)
CloseNetworkConnection(ID_Cnx)
StatusBarText(0, 0, "Disconnected From " + IP$) ;
DisableGadget(#Btn_Send, 1)
Name$ = "ID_" + Str(Random(99, 1))
SetGadgetText(#Btn_Connect, "Connecter")
EndProcedure
Procedure Send_Txt()
TxtToSend$ = Name$ + " | " + GetGadgetText(#txt)
Nb = SendNetworkString(ID_Cnx, TxtToSend$, #PB_UTF8)
TxtToSend$ = ""
SetGadgetText(#txt, "")
EndProcedure
Procedure Change_Name()
Protected Old_Name$ = Name$
Name$ = LSet(GetGadgetText(#Str_Name), 8, " ")
AddGadgetItem(#Dialog, -1, Old_Name$ + " is now named: " + Name$)
SendNetworkString(ID_Cnx, "/Change " + Name$, #PB_UTF8)
EndProcedure
; Connect()
Repeat
Win_Event = WaitWindowEvent()
; Events WINDOWS
Select Win_Event
Case #PB_Event_CloseWindow
End
Case #PB_Event_Menu
If EventMenu() = 13
Send_Txt()
EndIf
Case #PB_Event_SizeWindow
ResizeGadgetsWin()
Case #PB_Event_Timer
If EventTimer() = #Refresh
While WindowEvent() : Wend
EndIf
Case #PB_Event_Gadget
Select EventGadget()
Case #Btn_Connect
Connect()
Case #Btn_Quit
Disconnect()
End
Case #Btn_Send
If ID_Cnx
Send_Txt()
Else
AddGadgetItem(#Dialog, -1, "Connection Lost")
EndIf
Case #Btn_Name
Change_Name()
EndSelect
EndSelect ; (Win_Event)
; info Events RESEAU
If GetGadgetText(#Btn_Connect) = "Disconnect"
Net_Event = NetworkClientEvent(ID_Cnx)
EndIf
Select Net_Event
Case #PB_NetworkEvent_Data
;While WindowEvent() : Wend
FillMemory(*Buffer, 1000)
ReceiveNetworkData(ID_Cnx, *Buffer, 1000)
Received_Txt$ = PeekS(*Buffer, -1, #PB_UTF8)
AddGadgetItem(#Dialog, -1, LTrim(Received_Txt$))
Received_Txt$ = ""
EndSelect
Until Quit = 1
AddGadgetItem(#Dialog, -1, "End of Connection")
CloseNetworkConnection(ID_Client)
End
Global_Error:
MessageRequester("Error", ErrorMessage(), 16)
Je manipule assez mal la gestion directe de la mémoire (Peeks et autres, vidage des zones mémoire) si des spécialistes peuvent vérifier/améliore/fiabiliser ça: welcome.
Enjoy
