' nested enum for supported states
public enum status
listening
connected
end enum 'status
' start up the talker's functionality
public sub start()
threadpool.queueuserworkitem(new system.threading.waitcallback(addressof establishsocket))
end sub 'start
' establish a socket connection and start receiving
private sub establishsocket(byval state as object)
try
' if not client, setup listner
if not client then
dim listener as socket
try
listener = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp)
listener.blocking = true
listener.bind(endpoint)
setstatus(status.listening)
listener.listen(0)
socket = listener.accept()
listener.close()
catch e as socketexception
' if there is already a listener on this port try client
if e.errorcode = 10048 then
client = true
endpoint = new ipendpoint(dns.resolve("127.0.0.1").addresslist(0), endpoint.port)
else
raiseevent notifications(notification.errornotify, "error initializing socket:" & controlchars.crlf & e.tostring())
end if
end try
end if
' try a client connection
if client then
dim temp as new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp)
temp.blocking = true
temp.connect(endpoint)
socket = temp
end if
' if it all worked out, create stream objects
if not (socket is nothing) then
setstatus(status.connected)
dim stream as new networkstream(socket)
reader = new streamreader(stream)
writer = new streamwriter(stream)
raiseevent notifications(notification.initialized, me)
else
raiseevent notifications(notification.errornotify, "failed to establish socket")
end if
' start receiving talk
' note: on w2k and later platforms, the networkstream.read()
' method called in receivetalke will generate an exception when
' the remote connection closes. we handle this case in our
' catch block below.
receivetalk()
' on win9x platforms, networkstream.read() returns 0 when
' the remote connection closes, prompting a graceful return
' from receivetalk() above. we will generate a notification.end
' message here to handle the case and shut down the remaining
' wintalk instance.
raiseevent notifications(notification.endnotify, "remote connection has closed.")
catch e as ioexception
dim sockexcept as socketexception = ctype(e.innerexception, socketexception)
if not (sockexcept is nothing) and 10054 = sockexcept.errorcode then
raiseevent notifications(notification.endnotify, "remote connection has closed.")
else
raiseevent notifications(notification.errornotify, "socket error:" & controlchars.crlf & e.message)
end if
catch e as exception
raiseevent notifications(notification.errornotify, "socket error:" & controlchars.crlf & e.message)
end try
end sub 'establishsocket
' send text to remote connection
public sub sendtalk(byval newtext as string)
dim send as string
' is this an append
if prevsendtext.length <= newtext.length and string.compareordinal(newtext, 0, prevsendtext, 0, prevsendtext.length) = 0 then
dim append as [string] = newtext.substring(prevsendtext.length)
send = string.format("a{0}:{1}", append.length, append)
' or a complete replacement
else
send = string.format("r{0}:{1}", newtext.length, newtext)
end if
' send the data and flush it out
writer.write(send)
writer.flush()
' save the text for future comparison
prevsendtext = newtext
end sub 'sendtalk
' send a status notification
private sub setstatus(byval statusobj as status)
me.statusobj = statusobj
raiseevent notifications(notification.statuschange, statusobj)
end sub 'setstatus
' receive chat from remote client
private sub receivetalk()
dim commandbuffer(19) as char
dim onebuffer(0) as char
dim readmode as integer = 1
dim counter as integer = 0
dim textobj as new stringbuilder()
while readmode <> 0
if reader.read(onebuffer, 0, 1) = 0 then
readmode = 0
goto continuewhile1
end if
select case readmode
case 1
if counter = commandbuffer.length then
readmode = 0
goto continuewhile1
end if
if onebuffer(0) <> ":"c then
commandbuffer(counter) = onebuffer(0)
counter = counter + 1
else
counter = convert.toint32(new string(commandbuffer, 1, counter - 1))
if counter > 0 then
readmode = 2
textobj.length = 0
else
if commandbuffer(0) = "r"c then
counter = 0
prevreceivetext = string.empty
raiseevent notifications(notification.received, prevreceivetext)
end if
end if
end if
case 2
textobj.append(onebuffer(0))
counter = counter - 1
if counter = 0 then
select case commandbuffer(0)
case "r"c
prevreceivetext = textobj.tostring()
case else
prevreceivetext += textobj.tostring()
end select
readmode = 1
raiseevent notifications(notification.received, prevreceivetext)
end if
case else
readmode = 0
goto continuewhile1
end select
continuewhile1:
end while
end sub 'receivetalk
private socket as socket
private reader as textreader
private writer as textwriter
private client as boolean
private endpoint as ipendpoint
private prevsendtext as string
private prevreceivetext as string
private statustext as string
private statusobj as status
end class 'talker