April 26, 2024, 04:27:11 PM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


UDP sample to create a chat program

Started by JoaoAfonso, December 11, 2007, 04:21:31 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

JoaoAfonso

Ahoy.

I have the intention of create a game which need a communication system. The comm skeleton I believe I must use to better aid me is the one explained in UDP sample that comes in EB starting projects.

The idea is have a server (my program) listening a port, and clients (with telnet, or something) will connect via IP/name to the port my program is listening.
Each time someone connects to the server it creates a list of persons connected (not implemented yet).
Each time someone sends any input to the server, it receives the message and broadcasts to every person connected (not implemented yet).

I wrote this code based in UDP example, as told before, but couldn't make it work (it starts up, I believe it listens in the port I defined, but does not open a connection):

'COMMUNICATION DEFINITIONS - START
$use "Ws2_32.lib"
Declare Import, WSACleanup(), int
Declare Import, bind(sock:uint, SOCKADDR_IN:pointer, saLen:int),int
Declare Import, WSAStartup(version:word, WSADATA:pointer), int
Declare Import, closesocket(s:uint), int
Declare Import, inet_ntoa(inn:int), string
Declare Import, sendto(sock:uint, SendBuf:pointer, BufLen:int, flags/*0*/:int, SOCKADDR_IN:pointer, SOCKADDR_IN_len:int),int/*sent or SOCKET_ERROR*/
Declare Import, recvfrom(sock:uint, SendBuf:pointer, BufLen:int, opt flags/*0*/:int, opt SOCKADDR_IN=NULL:pointer, opt SOCKADDR_IN_len=0:pointer),int
Declare Import, socket(opt af=AF_INET:uint, opt s_type=SOCK_STREAM:int, opt protocol=IPPROTO_TCP:int), uint
Declare Import, htons(hostshort:word), int
Declare Import, inet_addr(cp:pointer), uint
Declare Import, CreateThread(lpThreadAttributes:pointer, dwStackSize:int, lpStartAddress:uint, lpParameter:uint, dwCreationFlags:uint, lpThreadId:pointer),uint
Declare Import, gethostbyname(hostname:string),HostEnt
Declare Import, gethostname(hostname:string, namelen :int),int
Declare Import, _RtlZeroMemory ALIAS RtlZeroMemory(dest AS POINTER,numBytes AS INT)
Declare Import, _CreateThread ALIAS CreateThread(lpThreadAttributes AS SECURITY_ATTRIBUTES,dwStackSize AS INT,lpStartAddress AS INT BYREF,lpParameter AS POINTER,dwCreationFlags AS INT,lpThreadId AS INT BYREF),INT
Declare Import, _CloseHandle ALIAS CloseHandle(hObject AS INT),INT

DECLARE udpStartup(),int
DECLARE udpShutdown()
DECLARE udpSendText(ip as string, port as UInt, text as string),int
DECLARE udpListen(port as UInt)

Const INVALID_SOCKET         = -1
Const AF_INET                = 2
Const INADDR_NONE            = -1
Const INADDR_ANY             = 0
Const SOCK_DGRAM             = 2
Const IPPROTO_UDP            = 17
Const WSADESCRIPTION_LEN     = 256
Const WSASYS_STATUS_LEN      = 128
Const UDM_SETRANGE32         = 0x400+111
Const WSA_MESSAGE  = 65535
Const MAX_MSG_SIZE = 1024
Const WSVER1 = 0x101
Const WSVER2 = 0x202
WSA_NoName = "Unknown"

Type SOCKADDR_IN
WORD sin_family
WORD sin_port
UINT sin_addr
CHAR sin_zero[8]
EndType

Type WSADataType
   DEF wVersion:WORD 
   DEF wHighVersion:WORD 
   DEF strDescription[257]:ISTRING 
   DEF strSystemStatus[129]:ISTRING 
   DEF iMaxSockets:INT 
   DEF iMaxUdpDg:INT 
   DEF lpVendorInfo:POINTER 
EndType

Type HostEnt
    DEF h_name :POINTER
    DEF h_aliases :POINTER
    DEF h_addrtype:WORD
    DEF h_length:WORD
    DEF addrlist :POINTER
EndType

UINT listen_sock, send_sock
'COMMUNICATION DEFINITIONS - END

DECLARE gdh(),string



OPENCONSOLE
PRINT "COMM SERVER"
IF udpstartup()=1
udpListen(5900)
ELSE
PRINT "Server could not startup."
ENDIF

DO

UNTIL inkey$<>""
udpshutdown()
CLOSECONSOLE
END


'------------EXTERNAL FUNCTIONS----------------------

Sub udpStartup(),int
'Starts up
DEF StartupData:WSADataType
if WSAStartup( WSVER2 , StartupData)=0
Return 1
EndIf
Return 0
EndSub

Sub udpShutdown()
'Shuts down
closesocket(listen_sock)
closesocket(send_sock)
listen_sock=0
WSACleanup()
Return
EndSub

Sub udpSendText(ip as string, port as UInt, text as string),int
'Sends text
INT  size, a, lines
sockaddr_in sa
if send_sock=0 then send_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
'preset the send-structure
_RtlZeroMemory(&sa, len(sa))
sa.sin_family = AF_INET
sa.sin_port   = htons(port)
sa.sin_addr   = inet_addr(ip)
size=len(sa)
a = sendto(send_sock, &text, len(text), 0, &sa, size)
Return 1
EndSub

Sub udpListen(port as UInt)
'Sets up a listening port
INT result, size
UINT threadID, thread_handle
sockaddr_in  sa
result = FALSE
'Close any existing listening port
if listen_sock
closesocket(listen_sock)
listen_sock = 0
endif
'Create the listening port
if listen_sock = 0 then listen_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
if listen_sock <> INVALID_SOCKET
size = len(sa)
_RtlZeroMemory(&sa, size)
sa.sin_family = AF_INET
sa.sin_port   = htons(port)
sa.sin_addr   = INADDR_ANY
'and bind it to a new thread to receive incoming data
if bind(listen_sock, sa, size) = 0
thread_handle = CreateThread(NULL, MAX_MSG_SIZE+64, &ThreadProc, listen_sock, 0, &threadID)
_CloseHandle(thread_handle)
result = thread_handle
endif
endif
return result
EndSub

'--------------INTERNAL FUNCTION - do not call -------------------

sub ThreadProc(sock:uint),int
'this function is running parallel (as thread),
'and will always receive data, then notify the parent window
INT size, received
sockaddr_in sa
ISTRING buff[MAX_MSG_SIZE]
received = 1
' if you close socket listen_sock then this thread will terminate
while received > 0
size = len(sa)
' the socket is in blocking mode, so don't worry
' about cpu usage
received = recvfrom(sock, buff, MAX_MSG_SIZE-1, 0, &sa, size)
if received > 0
' remove invalid data
buff[received] = 0
' and call the HandleMessage function
HandleMessage(inet_ntoa(sa.sin_addr),buff)
endif
wend
return 0
endsub

Sub HandleMessage(ip as string, text as string)
print gdh()+"> " + ip + ": " + text
EndSub

sub gdh(),string
return ucase$(DATE$("dd")+left$(time$,2)+mid$(time$,4,2)+date$("MMMyy"))
ENDSUB


If someone can point me what to do/fix, I would thank you very much. After being able to create a connection from a client to the server, I believe I can figure out how to handle multiple clients.

Thank you in advance.
JoÃÆ'ƒÂÃ,£o Afonso
Viriato
-----------------
Iberia MUD
www.iberiamud.com
iberiamud.com:5900

pistol350

Quote from: JoaoAfonso on December 11, 2007, 04:21:31 PM
The comm skeleton I believe I must use to better aid me is the one explained in UDP sample that comes in EB starting projects.

I'm still searching for that "UDP sample" in Ebasic Projects folder but can't find it.Could you Point it to me please ?
By the way, i remember a UDP sample code Written by Andrew in IBPro, in case this one is not the one you mentioned, i can post it here.

Regards,
Peter.
Regards,

Peter B.

sapero

December 13, 2007, 04:58:42 PM #2 Last Edit: December 13, 2007, 05:02:49 PM by sapero
I'm wondering why do you not use the winapi include pack - simple $include "winsock2.inc" instead all the declares and types.
UDP is a connectionless protocol, it means that you send single packets to someone without connecting.

Here is the modified version - it can receive and send broadcast messages, so if a client is searching for the server (inside LAN) it should send a broadcast packet with specified destination port (see sub udpSendBroadcast) then the server after analysing the packet should send a ack packet to client. This can be called 'connection estabilished'
$include "winsock2.inc"
autodefine "OFF"


'COMMUNICATION DEFINITIONS - START
Declare Import, _inet_ntoa alias inet_ntoa(inn:int), string /* original api returns a pointer to string */
const MAX_MSG_SIZE = 1024

SOCKET g_socket /* you need only one socket */
HANDLE g_thread_handle
'COMMUNICATION DEFINITIONS - END


OPENCONSOLE
PRINT "COMM SERVER"

IF udpstartup()=1
udpListen(99)
ELSE
PRINT "Server could not startup."
ENDIF

udpSendText("127.0.0.1", 99, "Hello world [normal]")

DO
Sleep(1000)
udpSendBroadcast(99, "Hello world [broadcast]")
UNTIL inkey$<>""
udpshutdown()
CLOSECONSOLE
END


'------------EXTERNAL FUNCTIONS----------------------

Sub udpStartup(),int
'Starts up
DEF StartupData:WSADATA
if WSAStartup( 0x202 , StartupData)=0
Return 1
EndIf
Return 0
EndSub



Sub udpShutdown()
'Shuts down
closesocket(g_socket)
g_socket=0
WSACleanup()

/* wait until the thread terminates - it will do it after closesocket */
if (g_thread_handle)
WaitForSingleObject(g_thread_handle, INFINITE)
CloseHandle(g_thread_handle)
g_thread_handle = 0
endif

Return
EndSub



Sub udpSendText(ip as string, port as UInt, text as string),int
'Sends text
sockaddr_in sa
'preset the send-structure
ZeroMemory(&sa, len(sa))
sa.sin_family = AF_INET
sa.sin_port   = htons(port)
sa.sin_addr.s_un.s_addr = inet_addr(ip)

Return sendto(g_socket, &text, len(text), 0, &sa, len(sa))
EndSub


sub udpSendBroadcast(port as UInt, text as string),int
Return udpSendText("255.255.255.255", port, text)
EndSub


Sub udpListen(port as UInt),int
'Sets up a listening port
INT result, size
UINT threadID
sockaddr_in  sa
result = FALSE

'Create the listening port
g_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
if g_socket <> INVALID_SOCKET
size = len(sa)
/* Enables transmission and receipt of broadcast messages on the socket */
setsockopt(g_socket, SOL_SOCKET, SO_BROADCAST, &size, 4)

ZeroMemory(&sa, size)
sa.sin_family = AF_INET
sa.sin_port   = htons(port)
/* not required for INADDR_ANY(=0) */
'sa.sin_addr.s_un.s_addr   = INADDR_ANY

'and bind it to a new thread to receive incoming data
if bind(g_socket, sa, size) = 0
/* thread parameter is defined as pointer, so a dummy "*<POINT>" will pass g_socket by value */
g_thread_handle = CreateThread(NULL, 0, &ThreadProc, *<POINT>g_socket, 0, &threadID)
/* do not close it here, but in udpShutdown */
'CloseHandle(g_thread_handle)
result = g_thread_handle
endif
endif
return result
EndSub

'--------------INTERNAL FUNCTION - do not call -------------------

sub ThreadProc(sock:uint),int

'this function is running parallel (as thread),
'and will always receive data, then notify the parent window

INT size, received
sockaddr_in sa
ISTRING buff[MAX_MSG_SIZE]

received = 1

' if you close socket g_socket then this thread will terminate
while received <> SOCKET_ERROR
size = len(sa)
' the socket is in blocking mode, so don't worry
' about cpu usage

received = recvfrom(sock, buff, MAX_MSG_SIZE-1, 0, &sa, &size)

if received > 0
' remove invalid data
buff[received] = 0
' and call the HandleMessage function
HandleMessage(_inet_ntoa(sa.sin_addr.s_un.s_addr), buff)
endif
wend

return 0
endsub



Sub HandleMessage(ip as string, text as string)
/* using comma's youre reducing memory allocation */
print gdh(), "> ", ip, ": ", text
EndSub



sub gdh(),string
return ucase$(DATE$("dd")+left$(time$,2)+mid$(time$,4,2)+date$("MMMyy"))
ENDSUB



BTW I would...
Sub udpStartup(),BOOL
'Starts up
DEF StartupData:WSADATA
return WSAStartup( 0x202 , &StartupData)=0
EndSub

JoaoAfonso

I will add the udp sample in this post.

Sapero, I am not used to EB yet, so I am using for sure useless code. If that helps me understand EB better, I wouldn't mind :)
Anyway, I am trying to run your example, but when compiling, it returns more than 80 errors... I checked and no winsock.inc file in my includes. Where to get it?
My idea is to receive text and broadcast it to others, but also receive text and send it to a defined person. I have in mind a way to do it (list of persons that log in), but again, cannot make even a single connection.
Is there a better way to make connections clients-server?
Thank you in advance
JoÃÆ'ƒÂÃ,£o Afonso
Viriato
-----------------
Iberia MUD
www.iberiamud.com
iberiamud.com:5900

sapero

December 13, 2007, 07:25:05 PM #4 Last Edit: December 13, 2007, 07:28:11 PM by sapero
In my sig you find a link to headers installer download EBasic SDK Headers or just browse to http://www.ionicwind.com download/other section. It will install the base API + COM headers.

UDP is very good for short messages, with just one socket you can handle milions of clients. With TCP only one client is allowed per socket, and the socket must be connected.
Your function udpSendText will send message to specified computer, my udpSendBroadcast will send the message to all computers in current subnet that are listening on same port as the server sends message.
I have created a filesystem plugin for Total Commander that allows me to access files on virually running 10x win95. The main communication is coded with UDP packets: GetStartTime, FindFirstFile, GetFile... but for file transfer I have used TCP because UDP dropped some packets. After entering the root of plugin's filesystem, a packet "who is online" is broadcasted then the plugin is waiting for replies. A server running inside virtual win95 replies to the plugin with "system started at date,time" then the plugin is able to display a list of active servers IP as folders accesible for browsing.

You'll need a custom protocol in order to be productive. Create a list of commands (as named constants) then associoate separate structure for each command, and one shared for all commands. The shared will be present in all packets.
like:

TYPE PACKET_HEADER
int PacketType  ' FUNCTION_***
int PacketLength  ' bidirectional
int ErrorCode    ' server->client
ENDTYPE

const FUNCTION_LOGIN_GET_SEED = 1
const FUNCTION_LOGIN_GET_SEED_REPLY = 2
const FUNCTION_LOGIN_PASSWORD = 3
const FUNCTION_LOGIN_PASSWORD_REPLY = 4
const FUNCTION_REGISTER_NEWUSER = 5

TYPE PACKET_LOGIN_GET_SEED  ' client->server
PACKET_HEADER header
string UserName
ENDTYPE

TYPE PACKET_LOGIN_GET_SEED_REPLY  ' server->client
PACKET_HEADER header
int PasswordSeed
ENDTYPE

TYPE PACKET_LOGIN_PASSWORD  ' client->server
PACKET_HEADER header
int PasswordHash /* generate from PasswordSeed and plain password */
ENDTYPE

TYPE PACKET_LOGIN_PASSWORD_REPLY  ' server->client
PACKET_HEADER header
ENDTYPE

TYPE PACKET_MESSAGE  ' client->server
PACKET_HEADER header /* .Length = len(PACKET_HEADER) + 4 + len(text) */
int ToUserId
string text
ENDTYPE


... and so on. For every client define a status variable: NotLogged, ReceivedSeed, LoggedIn ...
Set initial client status to NotLogged, after the client sends the first packet with user-name, change status to ReceivedSeed and reply with password seed if the user as found in database, wait for password hash packet, then accept or disconnect. If accepted, set status to LoggedIn that allows to send messages

pistol350

This code From Andrew may be useful
It creates a very simple chat application


/*
Hi all. This was on the last forum, so just in case you need it, here it is again. No changes have been made. If somebody wants to make this into a control pak, feel free, but I think Mike's adding network support to his 2d pack eventually, so it might become rapidly obsolete!

In case you don't know, UDP is a network protocol, like TCP, but faster and not as safe. It's good for games where fast traffic is necessary. You'll need to error check for dropped packets though.

Functions included are:

udpStartup()
returns 1 if successful, 0 if not

udpShutdown()
Call this when you've finished with the network.

udpGetMyIP()
returns local machine's IP address as a string

udpGetMyName()
returns local machine's host name as a string

udpResolveIP()
returns the ip address of a host name

udpSendText(ip,port,text)
sends <text> to <ip> on <port>

udpListen(port)
listens on <port> and calls the HandleMessage function in your app

Your HandleMessage function must take as its parameters two strings - ip, and text
eg.

Code:
Sub HandleMessage(ip as string, text as string)
print "User IP = " + ip
print "User Message: " + text
Return
EndSub
*/


$include "udp.inc"

string strWho,strIP,strMessage
UInt SendPort

SendPort=31415 : 'can be any number up to 65535
strIP="Unknown"

OpenConsole   
udpStartup()
udpListen(SendPort)   
Print "Your name is " + udpGetMyName()   
Print "and your ip is " + udpGetMyIP()   
while strIP="Unknown"     
Input "Who are you talking to? :",strWho     
If strWho<>""         
strIP=udpResolveIP(strWho)         
Print strWho+"'s IP is "+strIP     
EndIf   
EndWhile   
Print "Type away. Send a blank message to quit."   
strMessage="A"   
While strMessage<>""     
Input "",strMessage     

If strMessage<>""         
udpSendText(strIP,SendPort,strMessage)     
EndIf   
EndWhile   
udpShutdown()

CloseConsole
'The following function is requried if you use the udpListen function
Sub HandleMessage(ip as string, txt as string)   
'Put what you like in here to handle the message   
Color 10,0   
Print ip + " says " + txt   
Color 7,0   
Return
EndSub
Regards,

Peter B.

JoaoAfonso

Ahoy.
Last week I was out and just today I could use a computer. Saw the comments and I must test and experiment code today/tomorrow, but just by reading the last posts I believe I will be able to figure out and code what I am wanting to.

Thanks for all your aid
JoÃÆ'ƒÂÃ,£o Afonso
Viriato
-----------------
Iberia MUD
www.iberiamud.com
iberiamud.com:5900

JoaoAfonso

December 21, 2007, 06:43:46 AM #7 Last Edit: December 21, 2007, 06:56:19 AM by JoaoAfonso
Ok, with your inputs, and checking and trying to translate other workable CB examples, I added to udp.inc the following:
DECLARE IMPORT,WSAAsyncSelect(s:INT, hWnd:INT, wMsg:INT, lEvent:INT),INT
setid "WinsockListenEvent",   1025
setid "WinsockClientEvent",   1026
setid "FD_READ",      0x1
setid "FD_WRITE",      0x2
setid "FD_OOB",      0x4
setid "FD_ACCEPT",      0x8
setid "FD_CONNECT",      0x10
setid "FD_CLOSE",      0x20
def SelectOps:INT
SelectOps=@FD_READ|@FD_WRITE|@FD_ACCEPT|@FD_CONNECT|@FD_CLOSE


My line number 76 is

WSAAsyncSelect(listen_sock,d1,@WinsockListenEvent,SelectOps)

but when running, EB give the following error:

Compiling...
ccom.eba
File: C:\Documents and Settings\EB\ccom.eba (76) no appropriate conversion exists - )
Error(s) in compiling "C:\Documents and Settings\EB\ccom.eba"


I am trying to make that chat client running a server program in my computer with other persons connecting using, lets say, telnet. Got this code from previous IB STD/CB code, and checked in MS site that
QuoteThe WSAAsyncSelect function requests Windows message-based notification of network events for a socket.

Forgot to say I changed from console to a dialog because I think this function I need, wsaasyncselect, need a window/dialog to work (how can I call a console handle?).

maybe to be easier to understand, I want that chat communication program to be part of a larger program. Code is this (commdefs.inc=edited udp.inc):

$include "commdefs.inc"



DECLARE gdh(),string
DECLARE printf(text:string)

def d1:dialog
def porta,addr:INT
const maxsockets=10
INT socktable[maxsockets]


CREATEDIALOG d1,0,0,700,500,0x80CA0080,0,"Server machine",&HandlerUser
CONTROL d1,@EDIT,"",0,0,700,500,0x50A00004,1
CONTROL d1,@LISTVIEW,"",-100,-100,0,0,@LVSREPORT|@LVSSINGLESEL|@LVSSHOWSELALWAYS,2000
CONTROL d1,@LISTVIEW,"",-100,-100,0,0,@LVSREPORT|@LVSSINGLESEL|@LVSSHOWSELALWAYS,2500
CONTROL d1,@LISTVIEW,"",-100,-100,0,0,@LVSREPORT|@LVSSINGLESEL|@LVSSHOWSELALWAYS,3000
CONTROL d1,@LISTVIEW,"",-100,-100,0,0,@LVSREPORT|@LVSSINGLESEL|@LVSSHOWSELALWAYS,3500

porta=5900

showdialog d1 : Ready=1
waituntil Ready=0
'for addr=0 to MaxSockets-1
'WSAAsyncSelect(SockTable[addr], d1, 0, 0)
'closesocket(SockTable[addr])
'next addr
udpshutdown()
closedialog d1
END


SUB HandleMessage(ip as string, txt as string)   
PRINTF(ip + " says " + txt)
ENDSUB

sub gdh(),string
return ucase$(DATE$("dd")+left$(time$,2)+mid$(time$,4,2)+date$("MMMyy"))
ENDSUB

sub printf(text:string)
if GETCONTROLTEXT(d1,1)=""
setcontroltext d1,1,text
ELSE
setcontroltext d1,1,getcontroltext(d1,1)+chr$(13)+chr$(10)+text
ENDIF
ENDSUB

sub HandlerUser
def event,x:int
def MessageToSend:string
select @class
case @idinitdialog
SETFONT d1,"Courier New",8,400,0,1
ENABLECONTROL d1,1,0
starttimer d1,200,3
centerwindow d1
case @idclosewindow
ready=0
' case @WinsockListenEvent
'event=@code
'gosub ClientWelcome
'case @WinsockClientEvent
'event=@code
'gosub ClientEvent
case @idtimer
select @code
case 3
                printf("Server - v0.1")
                                                                                printf("")
IF udpstartup()=1
WSAAsyncSelect(listen_sock,d1,@WinsockListenEvent,SelectOps)
udpListen(porta)
ELSE
PRINTF("Server could not startup.")
ENDIF
stoptimer d1,3
printf(gdh()+" Listening")
endselect
endselect
ENDSUB


Both select options @WinsockListenEvent and @WinsockClientEvent I won't try to translate before clarify this problem I am posting, but are the routines run when a listen/send information happens.

Thank in advance for any explanation.
JoÃÆ'ƒÂÃ,£o Afonso
Viriato
-----------------
Iberia MUD
www.iberiamud.com
iberiamud.com:5900

sapero

All api function require HWND, not a pointer to WINDOW or DIALOG type you passed as d1.
Your window has a '.hwnd' member, so pass it instead the structure:
WSAAsyncSelect(listen_sock, d1.hwnd, ...

JoaoAfonso

Ok, after Sapero comment I figured out what were my mistakes when translating my old code, and here is an incomplete skeleton of a working program (I am still working and shaping it!):

commdefs.inc
$USE "wsock32.lib"
$USE "ws2_32.lib"
$USE "user32.lib"

DECLARE Import,WSACleanup(), int
DECLARE Import,WSAStartup(wVR :int, lpWSAD:MEMORY),int
DECLARE Import,socket(af:int,s_type:int,protocol:int),int
DECLARE Import,bind(s:INT, addr:pointer, namelen:INT),INT
DECLARE Import,htons(hostshort:word),int
DECLARE Import,htonl(hostlong:INT),INT
DECLARE Import,listen(s:INT, backlog:INT),INT
DECLARE Import,send(s:int,buf:string,buflen:int,flags:int),int
DECLARE Import,recv(s:int,buf:string,buflen:int,flags:int),int
DECLARE Import,accept(s:INT, addr:int, addrlen:INT),INT
DECLARE Import,closesocket(s:INT),INT
DECLARE Import,WSAAsyncSelect(s:INT, hWnd:INT, wMsg:INT, lEvent:INT),INT
DECLARE Import,WSAGetLastError(),INT
DECLARE Import,inet_ntoa(lIn:UINT),STRING
DECLARE Import,getpeername(s:INT, name:sockaddr, namelen:pointer),INT

TYPE WSADataType
DEF   wVersion:WORD
DEF   wHighVersion:WORD
DEF   strDescription[256]:ISTRING
DEF   strSystemStatus[128]:ISTRING
DEF   iMaxSockets:INT
DEF   iMaxUdpDg:INT
DEF   lpVendorInfo:int
ENDTYPE

TYPE sockaddr_in
DEF   sin_family:word
DEF   sin_port:word
DEF   sin_addr:int
DEF   sin_zero[8]:ISTRING
ENDTYPE

TYPE sockaddr_remote
DEF   sin_family:word
DEF   sin_port:word
DEF   sin_addr:int
DEF   sin_zero[8]:ISTRING
ENDTYPE
'-----------------------------------------
setid "WSVER2",         0x202
setid "NO_ERROR",      0
setid "AF_INET",      2
setid "SOCK_STREAM",   1
setid "IPPROTO_TCP",   6
setid "SOCKET_ERROR",   1
setid "INADDR_ANY",      0
setid "WinsockListenEvent",   1025
setid "WinsockClientEvent",   1026
setid "MaxConnections",   1
'-------------------------------WinSock notyfication
setid "FD_READ",      0x1
setid "FD_WRITE",      0x2
setid "FD_OOB",      0x4
setid "FD_ACCEPT",      0x8
setid "FD_CONNECT",      0x10
setid "FD_CLOSE",      0x20
DEF StructClient:sockaddr_remote
DEF StructServer:sockaddr_in
DEF StartupData:WSADataType
DEF tempsd,mHost:MEMORY
ALLOCMEM tempsd,1,len(StartupData)
def SelectOps:INT
DEF event,iResult,m_socket,ListenOut,Ready,WinSock,LenName:int
DEF INVALID_SOCKET,host,addr,RetSel,ReceivedBytes:int
DEF bufor[32768]:istring
DEF localname,UserName,MyIP:string
def BuforPointer,AddrPointer,NULLPointer:pointer

SelectOps=@FD_READ|@FD_WRITE|@FD_ACCEPT|@FD_CONNECT|@FD_CLOSE
INVALID_SOCKET=-1

DECLARE enviarmsg(outstring:string,socket:int)

sub enviarmsg(outstring:string,socket:int)
def i,row,StrLen,buffer_length:int
def tbiga$[3000],tbigb$[3000]:istring
row=75
tbiga$ = outstring
StrLen = Len(tbiga$)
while StrLen > Row
i = 0
while (mid$(tbiga$,Row - i,1)<>" ")
i = i + 1
endwhile
tbigb$ = left$(tbiga$,Row - i)
buffer_length=len(tbigb$+chr$(13)+chr$(10))
send(socket,tbigb$+chr$(13)+chr$(10),buffer_length+1,5)
tbiga$ = right$(tbiga$,StrLen -(Row - i))
StrLen = Len(tbiga$)
endwhile
tbigb$ = tbiga$
buffer_length=len(tbigb$+chr$(13)+chr$(10))
send(socket,tbigb$+chr$(13)+chr$(10),buffer_length+1,5)
ENDSUB


chat.eba
$INCLUDE "commdefs.inc"

DECLARE GetIP(socnum:int)
DECLARE GetSocket(socname:string), int
DECLARE AddSocket(socket:INT), INT
DECLARE ConnectedUsers(),string
DECLARE printf(text:string)

def d1:dialog
def port:INT
def linhascreen:string
const maxsockets=2
INT SockTable[maxsockets]
STRING SockTableName[maxsockets],SocketLC[maxsockets]

linhascreen="+-------------------------------------------------------------------------------+"
BuforPointer=bufor : NULLPointer=0 : LenName=255
AddrPointer = addr
port=9999

for a=1 to maxsockets-1
socktablename[a]="":socktable[a]=0:socketlc=""
next a

CREATEDIALOG d1,0,0,700,500,0x80CA0080,0,"Handle  caption",&HandlerUser
CONTROL d1,@EDIT,"",0,0,700,500,0x50A00004,1
CONTROL d1,@LISTVIEW,"",-100,-100,0,0,@LVSREPORT|@LVSSINGLESEL|@LVSSHOWSELALWAYS,2000
CONTROL d1,@LISTVIEW,"",-100,-100,0,0,@LVSREPORT|@LVSSINGLESEL|@LVSSHOWSELALWAYS,2500
CONTROL d1,@LISTVIEW,"",-100,-100,0,0,@LVSREPORT|@LVSSINGLESEL|@LVSSHOWSELALWAYS,3000
CONTROL d1,@LISTVIEW,"",-100,-100,0,0,@LVSREPORT|@LVSSINGLESEL|@LVSSHOWSELALWAYS,3500

showdialog d1 : Ready=1
waituntil Ready=0
for addr=0 to MaxSockets-1
WSAAsyncSelect(SockTable[addr], d1.hWnd, 0, 0)
closesocket(SockTable[addr])
next addr
WSACleanup()
freemem tempsd : closedialog d1
END

sub AddSocket(socket:INT),INT
def x,fail:int
fail=0
for x=0 to MaxSockets-1
if SockTable[x]<1
SockTable[x]=socket : fail=1 : return fail :' return 1 if succes, or 0 if fail
endif
next x
return fail :' return 1 if succes, or 0 if fail
ENDSUB

sub ConnectedUsers(),string
def x,users:int
users=0
for x=1 to MaxSockets-1
if SockTable[x]>0 then users=users+1
next x
return LTRIM$(str$(users))
ENDSUB

sub printf(text:string)
if GETCONTROLTEXT(d1,1)=""
setcontroltext d1,1,text
ELSE
setcontroltext d1,1,getcontroltext(d1,1)+chr$(13)+chr$(10)+text
ENDIF
ENDSUB

sub HandlerUser
def event,x:int
def MessageToSend:string
select @class
case @idinitdialog
SETFONT d1,"Courier New",8,400,0,1
ENABLECONTROL d1,1,0
starttimer d1,200,3
centerwindow d1
case @idclosewindow
ready=0
case @WinsockListenEvent
event=@code
gosub ClientWelcome
case @WinsockClientEvent
event=@code
'gosub ClientEvent - HAVEN'T LOOKED AT IT YET! I WILL WRITE THE ORIGINAL CODE BELOW, SO IT CAN BE CHANGED AND EDITED
case @idtimer
select @code
case 3
printf("Chat Server - v0.1")
printf("")
'START COMM STARTUP
iResult=WSAStartup(@WSVER2,tempsd)
if iResult<>@NO_ERROR then printf("WSAStartup ERROR")
m_socket=socket(@AF_INET, @SOCK_STREAM, @IPPROTO_TCP)
if m_socket=INVALID_SOCKET then printf("INVALID LISTENING SOCKET")
StructServer.sin_family = @AF_INET : StructServer.sin_addr = htonl(@INADDR_ANY) : StructServer.sin_port = htons(port)
if bind(m_socket,StructServer,len(StructServer))=@SOCKET_ERROR
closesocket(m_socket) : printf("INVALID LISTENING SOCKET")
endif
if listen(m_socket,@MaxConnections)=@SOCKET_ERROR
closesocket(m_socket) : printf("LISTENING ERROR")
ENDIF
AddSocket(m_socket)
WSAAsyncSelect(m_socket,d1.hWnd,@WinsockListenEvent,SelectOps)
'END COMM STARTUP
stoptimer d1,3 : starttimer d1,200,4
printf("Listening...")
case 4
setcaption d1,"Handle Caption - Connected Users: "+ConnectedUsers()
endselect
endselect
ENDSUB

sub ClientWelcome
def AcceptSocket,clientport:int
def texto:string
def gen1:file
AcceptSocket=accept(m_socket,StructClient.sin_addr,0)
if AcceptSocket<>INVALID_SOCKET
if AddSocket(AcceptSocket)<>0
if (openfile(gen1,GETSTARTPATH+"welcometext.dat","R")=0)
enviarmsg(" ",AcceptSocket)
do
if(read(gen1,texto) = 0)
enviarmsg(texto,AcceptSocket):'texto variables are plain text strings got from a ascii files
endif
until eof(gen1)
closefile gen1
endif
enviarmsg("",AcceptSocket)
enviarmsg("Your name?",AcceptSocket)
WSAAsyncSelect(AcceptSocket,d1.hWnd,@WinsockClientEvent,SelectOps)
else
printf(gdh()+" Server is full.")
enviarmsg("CHAT SERVER MESSAGE:",AcceptSocket)
enviarmsg("We have reached our maximum capability.",AcceptSocket)
enviarmsg("Hope to see you around later on!",AcceptSocket)
closesocket(AcceptSocket)
endif
endif
ENDSUB


and finally, the clientevent routine I haven't touched yet:
sub ClientEvent
def x:int
for x=1 to MaxSockets-1
if SockTable[x]>0
ReceivedBytes=recv(SockTable[x],Bufor,4096,0)
if ReceivedBytes>254
enviarmsg("That's a very big input!",SockTable[x])
socketlc[x]=""
next x
endif
if ReceivedBytes>0
if instr(left$(bufor,ReceivedBytes),chr$(8))<>0
if socketlc[x]<>"" then socketlc[x]=left$(socketlc[x],len(socketlc[x])-1):'in case DEL key is pressed and string not empty
else
if instr(left$(bufor,ReceivedBytes-1),chr$(13))=0
socketlc[x]=socketlc[x]+left$(bufor,ReceivedBytes)
else
if left$(bufor,ReceivedBytes),len(socketlc[x]+left$(bufor,ReceivedBytes))<2:'to eliminate some mistakes?
enviarmsg("Error in client trying to access chat server.",socktable[x])
socketlc[x]=""
next x
endif
socketlc[x]=left$(socketlc[x]+left$(bufor,ReceivedBytes),len(socketlc[x]+left$(bufor,ReceivedBytes))-2):'this variable is the text got from the client X
endif
endif
endif
      endif
next x
return
endsub


I try the code running the server in EB and connecting to localhost:9999 with telnet.

Hope it's usefull.
JoÃÆ'ƒÂÃ,£o Afonso
Viriato
-----------------
Iberia MUD
www.iberiamud.com
iberiamud.com:5900