Server.vb 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561
  1. Imports System.Net.Sockets
  2. Imports System.Net
  3. Imports System.IO
  4. Imports System.Collections.Concurrent
  5. Imports System.Threading
  6. Public Class Server
  7. Public errMsg As String
  8. ' Define the callback delegate type
  9. Public Delegate Sub ServerCallbackDelegate(ByVal bytes() As Byte, ByVal sessionID As Int32, ByVal dataChannel As Byte)
  10. ' Create Delegate object
  11. Public ServerCallbackObject As ServerCallbackDelegate
  12. Private Listener As TcpListener
  13. Private continue_running As Boolean = False
  14. Private blockSize As UInt16
  15. Private Port As Integer
  16. Private localAddr As IPAddress
  17. Private Mbps As UInt32
  18. Private newSessionId As Int32 = 0
  19. Public IsRunning As Boolean = False
  20. Private serverState As currentState = currentState.stopped
  21. Private enforceUniqueMachineIds As Boolean
  22. Private SessionCollection As New Sessions
  23. Private SessionCollectionLocker As New Object
  24. Public Class message
  25. Public bytes() As Byte
  26. Public dataChannel As Byte
  27. Public sessionID As Int32
  28. End Class
  29. Private Enum currentState
  30. err = -1
  31. stopped = 0
  32. running = 1
  33. idle = 2
  34. End Enum
  35. Private Class Sessions
  36. Private sessionCollection As New List(Of SessionCommunications)
  37. Private sessionLockObject As New Object
  38. Private reusableSessions As New Concurrent.ConcurrentQueue(Of Int32)
  39. Private newSessions As New Concurrent.ConcurrentQueue(Of TcpClient)
  40. Private newSessionId As Int32 = 0
  41. Public Function VerifyAndChangeUniqueMachineId(ByRef session As SessionCommunications, ByVal NewMachineId As String) As Boolean
  42. Dim machineIdIsUnique As Boolean = True
  43. SyncLock sessionLockObject
  44. For count = 0 to sessionCollection.Count -1
  45. If sessionCollection.Item(count).machineId = NewMachineId and sessionCollection.Item(count).machineIdValidated then
  46. machineIdIsUnique = False
  47. Exit For
  48. End If
  49. Next
  50. If machineIdIsUnique = True then
  51. session.machineId = NewMachineId
  52. session.machineIdValidated = True
  53. End If
  54. End SyncLock
  55. Return machineIdIsUnique
  56. End Function
  57. Public Sub AddSession(ByRef client As TcpClient, ByRef runThread As Thread, ByVal enforceUinqueIds As Boolean)
  58. SyncLock sessionLockObject
  59. Dim id As Int32 = GetReusableSessionID()
  60. Dim session As SessionCommunications = Nothing
  61. If id > -1 then
  62. session = sessionCollection.Item(id)
  63. 'session.Close()
  64. session.IsRunning = False
  65. session.shuttingDown = True
  66. session.machineId = ""
  67. session.machineIdValidated = False
  68. session.enforceUniqueMachineIds = enforceUinqueIds
  69. Try
  70. If session.theClient.Client isnot Nothing then session.theClient.Client.Dispose()
  71. Catch ex As Exception
  72. End Try
  73. session.theClient = client
  74. session.disConnect = False
  75. session.shuttingDown = False
  76. 'session = New SessionCommunications(client, id)
  77. 'session.enforceUniqueMachineIds = enforceUinqueIds
  78. 'session.machineId = ""
  79. 'session.machineIdValidated = False
  80. 'sessionCollection.Item(id) = Nothing
  81. 'sessionCollection.Item(id) = session
  82. Else
  83. id = newSessionId
  84. newSessionId += 1
  85. session = New SessionCommunications(client, id)
  86. sessionCollection.Add(session)
  87. End If
  88. If enforceUinqueIds = False then session.machineIdValidated = True
  89. runThread.Name = "Server session #" & id.ToString()
  90. runThread.Start(session)
  91. End SyncLock
  92. End Sub
  93. Public Sub CloseSession(ByRef session As SessionCommunications)
  94. SyncLock sessionLockObject
  95. session.IsRunning = False
  96. session.machineId = ""
  97. session.machineIdValidated = False
  98. ReuseSessionNumber(session.sessionID)
  99. End SyncLock
  100. End Sub
  101. Public Sub AddSession(ByVal theNewSession as SessionCommunications)
  102. SyncLock sessionLockObject
  103. If sessionCollection.Count > theNewSession.sessionID then
  104. sessionCollection.Item(theNewSession.sessionID) = Nothing
  105. sessionCollection.Item(theNewSession.sessionID) = theNewSession
  106. Else
  107. sessionCollection.Add(theNewSession)
  108. End If
  109. End SyncLock
  110. End Sub
  111. Public Function GetReusableSessionID() As Int32
  112. Dim sessionNumber As Int32 = -1
  113. If reusableSessions.TryDequeue(sessionNumber) then
  114. Return sessionNumber
  115. End If
  116. Return -1
  117. End Function
  118. Public Sub ReuseSessionNumber(ByVal sessionNumber As Int32)
  119. reusableSessions.Enqueue(sessionNumber)
  120. End Sub
  121. Public Function GetSession(ByVal sessionID As Int32, ByRef session As SessionCommunications) As Boolean
  122. Try
  123. If sessionCollection.Item(sessionID).machineIdValidated = False then Return False
  124. session = sessionCollection.Item(sessionID)
  125. If session.machineIdValidated = False then Return False
  126. If session is Nothing then Return False
  127. If Not session.IsRunning then Return False
  128. Return True
  129. Catch ex As Exception
  130. Return False
  131. End Try
  132. End Function
  133. Public Function GetSession(ByVal MachineID As String, ByRef session As SessionCommunications) As Boolean
  134. session = Nothing
  135. SyncLock sessionLockObject
  136. For Each connectedSession In sessionCollection
  137. If connectedSession.IsRunning And connectedSession.machineId = MachineID And connectedSession.machineIdValidated = True Then
  138. session = connectedSession
  139. Exit For
  140. End If
  141. Next
  142. End SyncLock
  143. If session is Nothing then Return False
  144. Return True
  145. End Function
  146. Public Function RemoveSession(ByRef session As SessionCommunications) As Boolean
  147. Dim retVal As Boolean = True
  148. SyncLock sessionLockObject
  149. Try
  150. sessionCollection.Remove(session)
  151. Catch ex As Exception
  152. retVal = False
  153. End Try
  154. End SyncLock
  155. Return retVal
  156. End Function
  157. Public Sub Broadcast(ByVal msg As message)
  158. Dim thisCopy As New List(Of SessionCommunications)
  159. SyncLock sessionLockObject
  160. For i As Int32 = 0 to sessionCollection.Count - 1
  161. thisCopy.Add(sessionCollection.Item(i))
  162. Next
  163. End SyncLock
  164. For i As Int32 = 0 To thisCopy.Count - 1
  165. If thisCopy.Item(i) IsNot Nothing AndAlso thisCopy.Item(i).IsRunning Then
  166. Try
  167. thisCopy.Item(i).sendQueue.Enqueue(msg)
  168. Catch ex As Exception
  169. End Try
  170. End If
  171. Next
  172. End Sub
  173. Public Function GetSessionCollection() As List(Of SessionCommunications)
  174. Dim thisCopy As New List(Of SessionCommunications)
  175. SyncLock sessionLockObject
  176. For i As Int32 = 0 to sessionCollection.Count - 1
  177. If sessionCollection.Item(i).machineIdValidated = True Then thisCopy.Add(sessionCollection.Item(i))
  178. Next
  179. End SyncLock
  180. Return thisCopy
  181. End Function
  182. Public Sub ShutDown()
  183. SyncLock sessionLockObject
  184. For Each session As SessionCommunications In sessionCollection
  185. Try
  186. If session IsNot Nothing Then session.Close()
  187. Catch ex As Exception
  188. End Try
  189. Next
  190. End SyncLock
  191. End Sub
  192. End Class
  193. Public Class SessionCommunications
  194. Public UserBytesToBeSentAvailable As Boolean = False
  195. Public UserBytesToBeSent As New MemoryStream
  196. Public UserOutputChannel As Byte
  197. Public SystemBytesToBeSentAvailable As Boolean = False
  198. Public SystemBytesToBeSent() As Byte
  199. Public SystemOutputChannel As Byte
  200. Public theClient As TcpClient
  201. Public IsRunning As Boolean = False
  202. Public remoteIpAddress As System.Net.IPAddress
  203. Public bytesRecieved() As Byte
  204. Public sessionID As Int32
  205. Public disConnect As Boolean = False
  206. Public bytesSentThisSecond As Int32 = 0
  207. Public bytesRecievedThisSecond As Int32 = 0
  208. Public fileBytesRecieved As Int64 = 0
  209. Public filebytesSent As Int64 = 0
  210. Public SendingFile As Boolean = False
  211. Public FileBeingSentPath As String
  212. Public IncomingFileSize As Int64
  213. Public IncomingFileName As String
  214. Public ReceivingFile As Boolean = False
  215. Public sendPacketSize As Boolean = False
  216. Public fileReader As FileStream
  217. Public fileWriter As AsyncUnbuffWriter
  218. Public ReceivedFilesFolder As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\ServerReceivedFiles"
  219. Public userName As String
  220. Public password As String
  221. Public paused As Boolean
  222. Public pauseSent As Boolean
  223. Public sendQueue As ConcurrentQueue(Of message)
  224. Public messageIn As MessageInQueue
  225. Public machineId As String
  226. Public shuttingDown As Boolean
  227. Public enforceUniqueMachineIds As Boolean
  228. Public machineIdValidated As Boolean
  229. Public Sub SendErrorMessage(ByVal message As String)
  230. message = "ERR: " & message
  231. If sendQueue is Nothing then sendQueue = New ConcurrentQueue(Of message)
  232. sendQueue.Enqueue(New message With { _
  233. .bytes = Utilities.StrToByteArray(message),
  234. .dataChannel = 254,
  235. .sessionID = sessionID
  236. })
  237. End Sub
  238. Public Sub QueueSystemMessage(ByVal message As String)
  239. If sendQueue is Nothing then sendQueue = New ConcurrentQueue(Of message)
  240. sendQueue.Enqueue(New message With { _
  241. .bytes = Utilities.StrToByteArray(message),
  242. .dataChannel = 254,
  243. .sessionID = sessionID
  244. })
  245. End Sub
  246. Public class MessageInQueue
  247. Public queue As New ConcurrentQueue(Of message)
  248. Private bgThread As New Threading.Thread(AddressOf Pump)
  249. Private running As Boolean
  250. Private callBack As ServerCallbackDelegate
  251. Public Sub New(ByRef _callBack As ServerCallbackDelegate)
  252. callBack = _callBack
  253. running = True
  254. bgThread.IsBackground = True
  255. bgThread.Start()
  256. End Sub
  257. Public Sub Close()
  258. running = False
  259. End Sub
  260. Private Sub Pump()
  261. Dim msg As message = Nothing
  262. While running
  263. If queue.Count > 0 then
  264. If queue.TryDequeue(msg) Then
  265. callBack(msg.bytes, msg.sessionID, msg.dataChannel)
  266. End If
  267. End If
  268. If queue.Count = 0 then Thread.Sleep(2)
  269. End While
  270. End Sub
  271. End Class
  272. Public Sub New(ByVal _theClient As TcpClient, ByVal _sessionID As Int32, Optional ByVal uniqueMachineIds As Boolean = True)
  273. theClient = _theClient
  274. sessionID = _sessionID
  275. paused = False
  276. pauseSent = False
  277. shuttingDown = False
  278. enforceUniqueMachineIds = uniqueMachineIds
  279. machineIdValidated = False
  280. End Sub
  281. Public Sub Close(Optional ByVal secondsToWaitForSendQueueToEmpty As Int32 = 3, Optional ByVal closeInBackground As Boolean = False)
  282. If closeInBackground = True Then
  283. If messageIn IsNot Nothing Then messageIn.Close()
  284. Dim bgThread As New Thread(AddressOf WaitClose)
  285. bgThread.Start(New TimeSpan(0, 0, secondsToWaitForSendQueueToEmpty))
  286. Return
  287. End If
  288. If messageIn IsNot Nothing Then messageIn.Close()
  289. Dim emptySendQueueTimeout = Now.Add(New TimeSpan(0, 0, secondsToWaitForSendQueueToEmpty))
  290. shuttingDown = True
  291. Try
  292. While (sendQueue.Count > 0) Or (UserBytesToBeSentAvailable = True)
  293. Thread.Sleep(5)
  294. If Now > emptySendQueueTimeout then Exit While
  295. End While
  296. Catch ex As Exception
  297. ' sendQueue is nothing... not interested in this error.
  298. End Try
  299. disConnect = True
  300. End Sub
  301. Private Sub WaitClose(ByVal o As Object)
  302. Dim emptySendQueueTimeout = Now.Add(CType(o, TimeSpan))
  303. shuttingDown = True
  304. Try
  305. While (sendQueue.Count > 0) Or (UserBytesToBeSentAvailable = True)
  306. Thread.Sleep(5)
  307. If Now > emptySendQueueTimeout then Exit While
  308. End While
  309. Catch ex As Exception
  310. ' sendQueue is nothing... not interested in this error.
  311. End Try
  312. Thread.Sleep(1000)
  313. disConnect = True
  314. End Sub
  315. End Class
  316. ''' <summary>
  317. ''' Returns a current copy of the server's internal list of sessions as a List(Of SessionCommunications). It is possible that some sessions may be inactive,
  318. ''' or disconnected. Care should be taken to check the session.isRunning before using one,
  319. ''' because inactive or disconnected sessions may be overwritten by new connections at any moment.
  320. ''' </summary>
  321. ''' <returns>List(Of SessionCommunications)</returns>
  322. ''' <remarks></remarks>
  323. Public Function GetSessionCollection() As List(Of SessionCommunications)
  324. Dim thisCollection As List(Of SessionCommunications) = SessionCollection.GetSessionCollection()
  325. Return thisCollection
  326. End Function
  327. ''' <summary>
  328. ''' Gets the session object associated with the sessionId. Returns Nothing for sessions where session.isRunning = False.
  329. ''' </summary>
  330. ''' <param name="sessionId"></param>
  331. ''' <returns>A TcpComm.Server.SessionCommunications object</returns>
  332. ''' <remarks></remarks>
  333. Public Function GetSession(ByVal sessionId As Int32) As SessionCommunications
  334. Dim theSession As SessionCommunications = Nothing
  335. ' Sessions that are not running are not returned, so that they're sendqueues are not
  336. ' accidently inflated.
  337. If SessionCollection.GetSession(sessionId, theSession) then Return theSession
  338. Return Nothing
  339. End Function
  340. ''' <summary>
  341. ''' Gets the first session object associated with the MachineID. Returns Nothing for sessions where session.isRunning = False.
  342. ''' </summary>
  343. ''' <param name="aMachineID"></param>
  344. ''' <returns>A TcpComm.Server.SessionCommunications object</returns>
  345. ''' <remarks></remarks>
  346. Public Function GetSession(ByVal aMachineID As String) As SessionCommunications
  347. GetSession = Nothing
  348. SessionCollection.GetSession(aMachineID, GetSession)
  349. Return GetSession
  350. End Function
  351. ' CallbackForm must implement an UpdateUI Sub.
  352. Public Sub New(ByVal callbackMethod As ServerCallbackDelegate, Optional ByVal _throttledBytesPerSecond As UInt32 = 9000000, Optional ByVal enforceUniqueMachineId As Boolean = True)
  353. Mbps = _throttledBytesPerSecond
  354. enforceUniqueMachineIds = enforceUniqueMachineId
  355. ' BlockSize should be 62500 or 63100, depending on requested speed.
  356. ' Excellent performance, and works great with throttling.
  357. Dim _blockSize As UInt16
  358. ' Get corrected blocksize for throttling.
  359. If Mbps < 300000 Then
  360. If Mbps > 16000 Then
  361. blockSize = 4000
  362. Else
  363. blockSize = CUShort((Mbps / 4))
  364. End If
  365. ElseIf Mbps > 300000 And Mbps < 500000 Then
  366. blockSize = 16000
  367. ElseIf Mbps > 500000 And Mbps < 1000000 Then
  368. blockSize = 32000
  369. Else
  370. Dim count As UInt32 = 0
  371. Dim aFourth As Decimal = 0
  372. If Mbps > 25000000 Then
  373. _blockSize = 63100
  374. Else
  375. _blockSize = 62500
  376. End If
  377. aFourth = CDec(Mbps / 4)
  378. Do
  379. count += _blockSize
  380. If (count + _blockSize) > aFourth Then
  381. Mbps = CUInt(count * 4)
  382. blockSize = _blockSize
  383. Exit Do
  384. End If
  385. Loop
  386. End If
  387. ' Initialize the delegate object to point to the user's callback method.
  388. ServerCallbackObject = callbackMethod
  389. End Sub
  390. Public Sub ThrottleNetworkBps(ByVal bytesPerSecond As UInteger)
  391. ' Default value is 9000000 Mbps. Ok throughput, and
  392. ' good performance for the server (low CPU usage).
  393. Mbps = bytesPerSecond
  394. End Sub
  395. ''' <summary>
  396. ''' This is a convienience function that handles the work of converting the text you would like to send to a byte array.
  397. ''' Passes back the return value and errMsg of SendBytes(). Returns True on success and False on falure. Check the errMsg
  398. ''' string for send failure explanations.
  399. ''' </summary>
  400. ''' <param name="textMessage"></param>
  401. ''' <param name="channel"></param>
  402. ''' <param name="sessionid"></param>
  403. ''' <param name="errMsg"></param>
  404. ''' <returns></returns>
  405. ''' <remarks></remarks>
  406. Public Function SendText(ByVal textMessage As String, Optional ByVal channel As Byte = 1, Optional ByVal sessionid As Int32 = -1, _
  407. Optional ByRef errMsg As String = "") As Boolean
  408. If textMessage = "" then
  409. errMsg = "Your text message must contain some text."
  410. Return False
  411. End If
  412. Return SendBytes(Utilities.StrToByteArray(textMessage), channel , sessionid, errMsg)
  413. End Function
  414. Public Function Start(ByVal prt As Integer, Optional ByRef errorMessage As String = "") As Boolean
  415. If serverState = currentState.running then
  416. errorMessage = "The server is already running."
  417. Return False
  418. End If
  419. serverState = currentState.idle
  420. Dim listenerThread As New Thread(AddressOf theListener)
  421. Try
  422. Port = prt
  423. localAddr = GetLocalIpAddress()
  424. continue_running = True
  425. IsRunning = True
  426. listenerThread.Name = "Server Listener Thread"
  427. listenerThread.Start()
  428. Catch ex As Exception
  429. errorMessage = ex.Message
  430. Return False
  431. End Try
  432. While serverState <> currentState.running
  433. Thread.Sleep(10)
  434. If serverState = currentState.err Or serverState = currentState.stopped Then
  435. errorMessage = errMsg
  436. Return False
  437. End If
  438. End While
  439. Return True
  440. End Function
  441. Public Sub Close()
  442. continue_running = False
  443. Try
  444. Listener.Stop()
  445. Catch ex As Exception
  446. End Try
  447. Try
  448. SessionCollection.ShutDown()
  449. Catch ex As Exception
  450. End Try
  451. IsRunning = False
  452. ServerCallbackObject(Utilities.StrToByteArray("Server Stopped."), -1, 255)
  453. serverState = currentState.stopped
  454. End Sub
  455. Private Function GetLocalIpAddress() As System.Net.IPAddress
  456. Dim strHostName As String
  457. Dim addresses() As System.Net.IPAddress
  458. strHostName = System.Net.Dns.GetHostName()
  459. addresses = System.Net.Dns.GetHostAddresses(strHostName)
  460. ' Find an IpV4 address
  461. For Each address As System.Net.IPAddress In addresses
  462. ' Return the first IpV4 IP Address we find in the list.
  463. If address.AddressFamily = AddressFamily.InterNetwork Then
  464. Return address
  465. End If
  466. Next
  467. ' No IpV4 address? Return the loopback address.
  468. Return System.Net.IPAddress.Loopback
  469. End Function
  470. Public Function GetBlocksize() As UInt16
  471. Return blockSize
  472. End Function
  473. ''' <summary>
  474. ''' Returns the size of the selected session's sendqueue. Returns -1 if the session is nothing, or session.isRunning = False.
  475. ''' CAUTION: Calling this function too often will result in decreased performance, and failing to call it at all may result
  476. ''' in an out of memory error. You can continue to add messages to a session's send queue for as long as the session is active
  477. ''' (isRunning = True), but that doesn't mean they are being sent as fast as you are adding them to the queue (or at all, for that matter).
  478. ''' </summary>
  479. ''' <param name="sessionId"></param>
  480. ''' <returns>An Int32</returns>
  481. ''' <remarks></remarks>
  482. Public Function GetSendQueueSize(ByVal sessionId As Int32) As Int32
  483. Dim sendQueueSize As Int32 = -1
  484. Dim session As SessionCommunications = Nothing
  485. If SessionCollection.GetSession(sessionId, session) Then
  486. If session IsNot Nothing AndAlso session.IsRunning Then
  487. GetSendQueueSize = session.sendQueue.Count
  488. End If
  489. End If
  490. Return sendQueueSize
  491. End Function
  492. Public Function GetFile(ByVal _path As String, ByVal sessionID As Int32) As Boolean
  493. Dim thisSession As SessionCommunications = Nothing
  494. If SessionCollection.GetSession(sessionID, thisSession) then
  495. If thisSession is Nothing then Return False
  496. If Not thisSession.IsRunning then Return False
  497. thisSession.sendQueue.Enqueue(New message With { _
  498. .bytes = Utilities.StrToByteArray("GFR:" & _path),
  499. .sessionID = sessionID,
  500. .dataChannel = 254
  501. })
  502. Else
  503. Return False
  504. End If
  505. Return True
  506. End Function
  507. Public Function SendFile(ByVal _path As String, ByVal sessionID As Int32) As Boolean
  508. Dim thisSession As SessionCommunications = Nothing
  509. If SessionCollection.GetSession(sessionID, thisSession) then
  510. If thisSession is Nothing then Return False
  511. If Not thisSession.IsRunning then Return False
  512. If thisSession.shuttingDown Then
  513. errMsg = "The session is shutting down, and will not accept any more outgoing messages."
  514. Return False
  515. End If
  516. thisSession.sendQueue.Enqueue(New message With { _
  517. .bytes = Utilities.StrToByteArray("SFR:" & _path),
  518. .sessionID = sessionID,
  519. .dataChannel = 254
  520. })
  521. Else
  522. Return False
  523. End If
  524. Return True
  525. End Function
  526. Public Function SendBytes(ByVal bytes() As Byte, Optional ByVal channel As Byte = 1, Optional ByVal sessionID As Int32 = -1, _
  527. Optional ByRef errMsg As String = "") As Boolean
  528. Dim foundSession As Boolean = False
  529. If channel = 0 Or channel > 250 Then
  530. errMsg = "Data can not be sent using channel numbers less then 1 or greater then 250."
  531. Return False
  532. End If
  533. Dim msg As message = New message()
  534. ReDim msg.bytes(bytes.Length -1)
  535. msg.dataChannel = channel
  536. msg.sessionID = sessionID
  537. Buffer.BlockCopy(bytes, 0, msg.bytes, 0, bytes.Length)
  538. If sessionID > -1 then
  539. Dim targetSession As SessionCommunications = Nothing
  540. If SessionCollection.GetSession(sessionID, targetSession) then
  541. If targetSession.shuttingDown then
  542. errMsg = "The session is shutting down, and will not accept any more outgoing messages."
  543. Return False
  544. End If
  545. targetSession.sendQueue.Enqueue(msg)
  546. Return True
  547. End If
  548. Else
  549. SessionCollection.Broadcast(msg)
  550. Return True
  551. End If
  552. errMsg = "The session you are trying to write to is no longer available."
  553. Return False
  554. End Function
  555. Public Function SendBytes(ByRef bytes() As Byte, ByVal offset As Int32, ByVal count As Int32, Optional ByVal channel As Byte = 1, Optional ByVal sessionID As Int32 = -1, _
  556. Optional ByRef errMsg As String = "") As Boolean
  557. Dim foundSession As Boolean = False
  558. If channel = 0 Or channel > 250 Then
  559. errMsg = "Data can not be sent using channel numbers less then 1 or greater then 250."
  560. Return False
  561. End If
  562. Dim msg As New message()
  563. ReDim msg.bytes(count - 1)
  564. Buffer.BlockCopy(bytes, offset, msg.bytes, 0, count)
  565. msg.dataChannel = channel
  566. msg.sessionID = sessionID
  567. If sessionID > -1 then
  568. Dim targetSession As SessionCommunications = Nothing
  569. If SessionCollection.GetSession(sessionID, targetSession) then
  570. If targetSession.shuttingDown then
  571. errMsg = "The session is shutting down, and will not accept any more outgoing messages."
  572. Return False
  573. End If
  574. targetSession.sendQueue.Enqueue(msg)
  575. Return True
  576. End If
  577. Else
  578. SessionCollection.Broadcast(msg)
  579. Return True
  580. End If
  581. errMsg = "The session you are trying to write to is no longer available."
  582. Return False
  583. End Function
  584. Public Function SendBytes(ByRef streamBytes As MemoryStream, Optional ByVal channel As Byte = 1, Optional ByVal sessionID As Int32 = -1, _
  585. Optional ByRef errMsg As String = "") As Boolean
  586. Dim foundSession As Boolean = False
  587. If channel = 0 Or channel > 250 Then
  588. errMsg = "Data can not be sent using channel numbers less then 1 or greater then 250."
  589. Return False
  590. End If
  591. Dim msg As message = New message()
  592. ReDim msg.bytes(streamBytes.Length -1)
  593. msg.dataChannel = channel
  594. msg.sessionID = sessionID
  595. streamBytes.Position = 0
  596. streamBytes.Read(msg.bytes, 0, msg.bytes.Length)
  597. If sessionID > -1 then
  598. Dim targetSession As SessionCommunications = Nothing
  599. If SessionCollection.GetSession(sessionID, targetSession) then
  600. If targetSession.shuttingDown then
  601. errMsg = "The session is shutting down, and will not accept any more outgoing messages."
  602. Return False
  603. End If
  604. targetSession.sendQueue.Enqueue(msg)
  605. Return True
  606. End If
  607. Else
  608. SessionCollection.Broadcast(msg)
  609. Return True
  610. End If
  611. errMsg = "The session you are trying to write to is no longer available."
  612. Return False
  613. End Function
  614. Private Function RcvBytes(ByVal data() As Byte, ByVal session As SessionCommunications, Optional ByVal dataChannel As Byte = 1) As Boolean
  615. ' dataType: >0 = data channel, > 250 = internal messages. 0 is an invalid channel number (it's the puck)
  616. If dataChannel < 1 Then
  617. RcvBytes = False
  618. Exit Function
  619. End If
  620. Try
  621. ' Check to see if our app is closing
  622. If Not continue_running Then Return False
  623. Dim passedData(data.Length - 1) As Byte
  624. Array.Copy(data,passedData, data.Length)
  625. If session isnot Nothing then
  626. session.messageIn.queue.Enqueue(New message With { _
  627. .bytes = passedData,
  628. .dataChannel = dataChannel,
  629. .sessionID = session.sessionID
  630. })
  631. Else
  632. ' These are internal system messages. There is no session associated with them
  633. ServerCallbackObject(data, -1, dataChannel)
  634. End If
  635. Catch ex As Exception
  636. ' An unexpected error.
  637. Debug.WriteLine("Unexpected error in server\RcvBytes: " & ex.Message)
  638. Return False
  639. End Try
  640. Return True
  641. End Function
  642. Private Function SendExternalSystemMessage(ByVal message As String, ByVal session As SessionCommunications) As Boolean
  643. session.SystemBytesToBeSent = Utilities.StrToByteArray(message)
  644. session.SystemOutputChannel = 254 ' Text messages / commands on channel 254
  645. session.SystemBytesToBeSentAvailable = True
  646. Return True
  647. End Function
  648. Private Function CheckSessionPermissions(ByVal session As SessionCommunications, ByVal cmd As String) As Boolean
  649. ' Your security code here...
  650. Return True
  651. End Function
  652. Private Function BeginFileSend(ByVal _path As String, ByVal session As SessionCommunications, ByVal fileLength As Long) As Boolean
  653. Try
  654. session.fileReader = New FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.None, AsyncUnbuffWriter.GetPageSize)
  655. session.SendingFile = True
  656. BeginFileSend = True
  657. Catch ex As Exception
  658. BeginFileSend = False
  659. _path = ex.Message
  660. session.SendingFile = False
  661. End Try
  662. Try
  663. If Not BeginFileSend Then session.fileReader.Close()
  664. Catch ex As Exception
  665. End Try
  666. End Function
  667. Private Sub GetMoreFileBytesIfAvailable(ByVal session As SessionCommunications)
  668. Dim bytesRead As Int32 = 0
  669. If session.SendingFile And Not session.SystemBytesToBeSentAvailable Then
  670. Try
  671. If session.SystemBytesToBeSent.Length <> blockSize Then ReDim session.SystemBytesToBeSent(blockSize - 1)
  672. bytesRead = session.fileReader.Read(session.SystemBytesToBeSent, 0, blockSize)
  673. If bytesRead <> blockSize Then ReDim Preserve session.SystemBytesToBeSent(bytesRead - 1)
  674. If bytesRead > 0 Then
  675. session.SystemOutputChannel = 253 ' File transfer from server to client
  676. session.SystemBytesToBeSentAvailable = True
  677. Else
  678. ReDim session.SystemBytesToBeSent(blockSize - 1)
  679. SendExternalSystemMessage("->Done", session) ' Send the client a completion notice.
  680. session.SendingFile = False
  681. ' Clean up
  682. session.fileReader.Close()
  683. session.fileReader = Nothing
  684. GC.GetTotalMemory(True)
  685. End If
  686. Catch ex As Exception
  687. SendExternalSystemMessage("ERR: " & ex.Message, session)
  688. ' We're finished.
  689. ReDim session.SystemBytesToBeSent(blockSize - 1)
  690. session.SendingFile = False
  691. session.fileReader.Close()
  692. End Try
  693. End If
  694. End Sub
  695. Private Function GetFilenameFromPath(ByRef filePath As String) As String
  696. Dim filePathParts() As String
  697. If filePath.Trim = "" Then Return ""
  698. Try
  699. filePathParts = Split(filePath, "\")
  700. GetFilenameFromPath = filePathParts(filePathParts.Length - 1)
  701. Catch ex As Exception
  702. filePath = ex.Message
  703. Return ""
  704. End Try
  705. End Function
  706. Private Function CreateFolders(ByVal _path As String) As Boolean
  707. CreateFolders = True
  708. Dim parts() As String
  709. Dim path As String = ""
  710. Dim count As Int32
  711. parts = Split(_path, "\")
  712. path = parts(0)
  713. For count = 1 To parts.Length - 2
  714. path += "\" & parts(count)
  715. Try
  716. If Not Directory.Exists(path) Then
  717. Directory.CreateDirectory(path)
  718. End If
  719. Catch ex As Exception
  720. End Try
  721. Next
  722. End Function
  723. Private Function BeginToReceiveAFile(ByVal _path As String, ByVal session As SessionCommunications) As Boolean
  724. Dim readBuffer As Int32 = 0
  725. session.ReceivingFile = True
  726. BeginToReceiveAFile = True
  727. session.fileBytesRecieved = 0
  728. Try
  729. CreateFolders(_path) ' Just a 256k write buffer for the server. Let's try to avoid memory problems...
  730. session.fileWriter = New AsyncUnbuffWriter(_path, True, 1024 * 256, session.IncomingFileSize)
  731. Catch ex As Exception
  732. _path = ex.Message
  733. session.ReceivingFile = False
  734. End Try
  735. If Not session.ReceivingFile Then
  736. Try
  737. session.fileWriter.Close()
  738. Catch ex As Exception
  739. End Try
  740. Return False
  741. End If
  742. End Function
  743. Private Function HandleIncomingFileBytes(ByRef bytes() As Byte, ByVal session As SessionCommunications) As Boolean
  744. Try
  745. session.fileWriter.Write(bytes, bytes.Length)
  746. HandleIncomingFileBytes = True
  747. Catch ex As Exception
  748. HandleIncomingFileBytes = False
  749. End Try
  750. End Function
  751. Private Sub FinishReceivingTheFile(ByVal session As SessionCommunications)
  752. Try
  753. session.fileWriter.Close()
  754. session.fileWriter = Nothing
  755. session.ReceivingFile = False
  756. Catch ex As Exception
  757. session.ReceivingFile = False
  758. End Try
  759. End Sub
  760. Private Sub HandleIncomingSystemMessages(ByVal bytes() As Byte, ByVal channel As Byte, ByVal session As SessionCommunications)
  761. If channel = 254 Then ' Text commands / messages passed between server and client
  762. Dim message As String = Utilities.BytesToString(bytes)
  763. Dim filePath As String
  764. Dim tmp As String = ""
  765. ' Get File Request: The client wants us to send them a file.
  766. If message.Length > 4 Then tmp = message.Substring(0, 4)
  767. If tmp = "GFR:" Then
  768. ' Get file path...
  769. filePath = message.Substring(4, message.Length - 4)
  770. ' Does it exist?
  771. If File.Exists(filePath) Then
  772. ' Do they have permission to get this file?
  773. If CheckSessionPermissions(session, "GFR") Then
  774. ' Are we already busy sending them a file?
  775. If Not session.SendingFile Then
  776. Dim _theFilesInfo As New FileInfo(filePath)
  777. If BeginFileSend(filePath, session, _theFilesInfo.Length) Then
  778. ' Send only the file NAME. It will have a different path on the other side.
  779. SendExternalSystemMessage("Sending:" & GetFilenameFromPath(filePath) & _
  780. ":" & _theFilesInfo.Length, session)
  781. Else
  782. ' FilePath contains the error message.
  783. SendExternalSystemMessage("ERR: " & filePath, session)
  784. End If
  785. Else
  786. ' There's already a GFR in progress.
  787. SendExternalSystemMessage("ERR: File: ''" & _
  788. session.FileBeingSentPath & _
  789. "'' is still in progress. Only one file " & _
  790. "may be transfered (from server to client) at a time.", session)
  791. End If
  792. Else
  793. ' This user doesn't have rights to "get" this file. Send an error.
  794. SendExternalSystemMessage("ERR: You do not have permission to receive files. Access Denied.", session)
  795. End If
  796. Else
  797. ' File doesn't exist. Send an error.
  798. SendExternalSystemMessage("ERR: The requested file can not be found by the server.", session)
  799. End If
  800. End If
  801. ' We're being informed that we will be receiving a file:
  802. If message.Length > 7 Then tmp = message.Substring(0, 8)
  803. If tmp = "Sending:" Then
  804. ' Strip away the headder...
  805. Dim msgParts() As String = Split(message, ":")
  806. session.IncomingFileSize = Convert.ToInt64(msgParts(2))
  807. session.IncomingFileName = msgParts(1)
  808. tmp = session.ReceivedFilesFolder & "\" & session.IncomingFileName
  809. SystemMessage("Receiving file: " & session.IncomingFileName)
  810. If Not BeginToReceiveAFile(tmp, session) Then
  811. SystemMessage("ERR: " & tmp)
  812. SendExternalSystemMessage("Abort->", session)
  813. End If
  814. End If
  815. If message.Length > 10 Then tmp = message.Substring(0, 10)
  816. If tmp = "MachineID:" Then
  817. message = message.Substring(10, message.Length - 10)
  818. message = message.Trim
  819. If enforceUniqueMachineIds = True then
  820. If message = "" then
  821. 'Send a connection Error:
  822. session.QueueSystemMessage("CERR:This server requires a unique Machine ID.")
  823. 'And a connection rejection notice:
  824. session.QueueSystemMessage("connection:rejected")
  825. ' Marke the session as invalidated, and set the machine name to nothing:
  826. session.machineIdValidated = False
  827. session.machineId = ""
  828. Else
  829. If SessionCollection.VerifyAndChangeUniqueMachineId(session, message) = False Then
  830. 'Send a connection Error:
  831. session.QueueSystemMessage("CERR:This server requires a unique Machine ID.")
  832. 'And a connection rejection notice:
  833. session.QueueSystemMessage("connection:rejected")
  834. ' Marke the session as invalidated, and set the machine name to nothing:
  835. session.machineIdValidated = False
  836. session.machineId = ""
  837. Else
  838. SystemMessage("Session#" & session.sessionID & " MachineID:" & session.machineId)
  839. session.QueueSystemMessage("connection:accepted")
  840. End If
  841. End If
  842. Else
  843. session.machineId = message
  844. SystemMessage("Session#" & session.sessionID & " MachineID:" & session.machineId)
  845. session.QueueSystemMessage("connection:accepted")
  846. End If
  847. End If
  848. If message = "<-Done" Then
  849. FinishReceivingTheFile(session)
  850. SystemMessage("<-Done")
  851. End If
  852. ' We've been notified that no file data will be forthcoming.
  853. If message = "Abort<-" Then
  854. WrapUpIncomingFile(session)
  855. SystemMessage("<-Aborted.")
  856. SendExternalSystemMessage("<-Aborted.", session)
  857. End If
  858. ' Send File Request: The client wants to send us a file.
  859. If message.Length > 4 Then tmp = message.Substring(0, 4)
  860. If tmp = "SFR:" Then
  861. If CheckSessionPermissions(session, "SFR") Then
  862. Dim parts() As String
  863. parts = Split(message, "SFR:")
  864. SendExternalSystemMessage("GFR:" & parts(1), session)
  865. Else
  866. ' This user doesn't have rights to send us a file. Send an error.
  867. SendExternalSystemMessage("ERR: You do not have permission to send files. Access Denied.", session)
  868. End If
  869. End If
  870. If message.Length > 4 Then tmp = message.Substring(0, 4)
  871. If tmp = "GDR:" Then ' Get Directory Request
  872. ' Send each file in the directory and all subdirectories.
  873. ' To be implemented in the future.
  874. End If
  875. If message.Length > 4 Then tmp = message.Substring(0, 4)
  876. If tmp = "ERR:" Then ' The client has sent us an error message.
  877. ' Pass it on up to the user.
  878. SystemMessage(message)
  879. End If
  880. ' New queue throttling code
  881. If message = "pause" Then
  882. session.paused = True
  883. End If
  884. If message = "resume" Then
  885. session.paused = False
  886. End If
  887. If message = "Abort->" Then
  888. Try
  889. session.SendingFile = False
  890. ReDim session.SystemBytesToBeSent(blockSize - 1)
  891. SendExternalSystemMessage("->Aborted.", session)
  892. SystemMessage("->Aborted.")
  893. session.fileReader.Close()
  894. Catch ex As Exception
  895. End Try
  896. End If
  897. ' The client is disconnecting. Close the connection gracefully...
  898. If message = "close" Then
  899. ' This will be caught by the try in the run sub, and execution
  900. ' will drop out of the communication loop immediately and
  901. ' begin the shutdown process.
  902. Throw New Exception("Shutting session down gracefully.")
  903. End If
  904. ElseIf channel = 253 Then ' File transfer from server to client
  905. ElseIf channel = 252 Then ' File transfer from client to server
  906. Try
  907. If session.ReceivingFile Then
  908. HandleIncomingFileBytes(bytes, session)
  909. session.fileBytesRecieved += bytes.Length
  910. End If
  911. Catch ex As Exception
  912. End Try
  913. ElseIf channel = 251 Then ' reserved.
  914. End If
  915. End Sub
  916. Private Function HandleOutgoingInternalSystemMessage(ByVal Stream As NetworkStream, _
  917. ByVal session As SessionCommunications) As Boolean
  918. Dim tmp(1) As Byte
  919. Dim _size As UShort
  920. 'Static OurTurn As Boolean = False
  921. HandleOutgoingInternalSystemMessage = False
  922. ' Create a one time outgoing system message to syncronize packet size.
  923. If Not session.sendPacketSize Then
  924. SendExternalSystemMessage("blocksize:" & blockSize.ToString, session)
  925. session.sendPacketSize = True
  926. End If
  927. GetMoreFileBytesIfAvailable(session)
  928. ' Handle outgoing system stuff here
  929. If session.SystemBytesToBeSentAvailable = True Then
  930. HandleOutgoingInternalSystemMessage = True
  931. If session.SystemBytesToBeSent.Length > blockSize Then
  932. ' Send Channel
  933. tmp(0) = session.SystemOutputChannel
  934. Stream.Write(tmp, 0, 1)
  935. ' Send packet size
  936. _size = blockSize
  937. tmp = BitConverter.GetBytes(_size)
  938. Stream.Write(tmp, 0, 2)
  939. ' Send packet
  940. Stream.Write(GetSome(session.SystemBytesToBeSent, blockSize, session.SystemBytesToBeSentAvailable, session), 0, _size)
  941. session.bytesSentThisSecond += 3 + blockSize
  942. Else
  943. ' Send Channel
  944. tmp(0) = session.SystemOutputChannel
  945. Stream.Write(tmp, 0, 1)
  946. ' Send packet size
  947. _size = Convert.ToUInt16(session.SystemBytesToBeSent.Length)
  948. tmp = BitConverter.GetBytes(_size)
  949. Stream.Write(tmp, 0, 2)
  950. ' Send packet
  951. Stream.Write(session.SystemBytesToBeSent, 0, _size)
  952. session.bytesSentThisSecond += 3 + _size
  953. session.SystemBytesToBeSentAvailable = False
  954. End If
  955. End If
  956. End Function
  957. Private Function HandleOutgoingUserData(ByVal Stream As NetworkStream, ByVal session As SessionCommunications) As Boolean
  958. Dim tmp(1) As Byte
  959. Dim _size As UShort
  960. Dim notify As Boolean = False
  961. Static packet(0) As Byte
  962. Dim msg As message = Nothing
  963. Dim shutSessionDown As Boolean = False
  964. If Not session.UserBytesToBeSentAvailable then
  965. If session.sendQueue.TryDequeue(msg) then
  966. session.UserBytesToBeSentAvailable = True
  967. session.UserBytesToBeSent.SetLength(0)
  968. session.UserBytesToBeSent.Write(msg.bytes, 0, msg.bytes.Length)
  969. session.UserBytesToBeSent.Position = 0
  970. session.UserOutputChannel = msg.dataChannel
  971. End If
  972. End If
  973. If session.disConnect Then
  974. SystemMessage("Session Stopped. (" & session.sessionID.ToString & ")")
  975. session.machineId = ""
  976. session.machineIdValidated = False
  977. SessionCollection.CloseSession(session)
  978. session.UserBytesToBeSentAvailable = True
  979. Dim closeMessage As Byte() = Utilities.StrToByteArray("close")
  980. session.UserBytesToBeSent.SetLength(0)
  981. session.UserBytesToBeSent.Write(closeMessage, 0, closeMessage.Length)
  982. session.UserBytesToBeSent.Position = 0
  983. session.UserOutputChannel = 254
  984. shutSessionDown = True
  985. End If
  986. If session.UserBytesToBeSentAvailable = True Then
  987. Try
  988. If (session.UserBytesToBeSent.Length - session.UserBytesToBeSent.Position) > blockSize Then
  989. ' Send Channel
  990. tmp(0) = session.UserOutputChannel
  991. Stream.Write(tmp, 0, 1)
  992. ' Send packet size
  993. _size = blockSize
  994. tmp = BitConverter.GetBytes(_size)
  995. Stream.Write(tmp, 0, 2)
  996. ' Send packet
  997. If packet.Length <> _size Then ReDim packet(_size - 1)
  998. session.UserBytesToBeSent.Read(packet, 0, _size)
  999. 'session.theClient.NoDelay = True
  1000. Stream.Write(packet, 0, _size)
  1001. session.bytesSentThisSecond += 3 + _size
  1002. ' Check to see if we've sent it all...
  1003. If session.UserBytesToBeSent.Length = session.UserBytesToBeSent.Position Then
  1004. session.UserBytesToBeSentAvailable = False
  1005. notify = True
  1006. End If
  1007. Else
  1008. ' Send Channel
  1009. tmp(0) = session.UserOutputChannel
  1010. Stream.Write(tmp, 0, 1)
  1011. ' Send packet size
  1012. _size = Convert.ToUInt16(session.UserBytesToBeSent.Length - session.UserBytesToBeSent.Position)
  1013. tmp = BitConverter.GetBytes(_size)
  1014. Stream.Write(tmp, 0, 2)
  1015. ' Send packet
  1016. If packet.Length <> _size Then ReDim packet(_size - 1)
  1017. session.UserBytesToBeSent.Read(packet, 0, _size)
  1018. 'session.theClient.NoDelay = True
  1019. Stream.Write(packet, 0, _size)
  1020. session.bytesSentThisSecond += 3 + _size
  1021. session.UserBytesToBeSentAvailable = False
  1022. notify = True
  1023. End If
  1024. Catch ex As Exception
  1025. ' Report error attempting to send user data.
  1026. Debug.WriteLine("Unexpected error in TcpCommServer\HandleOutgoingUserData: " & ex.Message)
  1027. End Try
  1028. ' Notify the user that the packet has been sent.
  1029. If notify Then SystemMessage("UBS:" & session.sessionID & ":" & session.UserOutputChannel)
  1030. ' This will drop execution out of the communications loop for this session, and
  1031. ' begin this session's shutdown process.
  1032. If shutSessionDown then
  1033. 'SystemMessage("Session Stopped. (" & session.sessionID.ToString & ")")
  1034. 'SessionCollection.CloseSession(session.sessionID)
  1035. Throw New Exception("Shutting session down gracefully.")
  1036. End If
  1037. Return True
  1038. Else
  1039. Return False
  1040. End If
  1041. End Function
  1042. Private Function GetSome(ByRef bytes() As Byte, ByVal chunkToBreakOff As Integer, _
  1043. ByRef bytesToBeSentAvailable As Boolean, ByVal session As SessionCommunications, _
  1044. Optional ByVal theseAreUserBytes As Boolean = False) As Byte()
  1045. Dim tmp(chunkToBreakOff - 1) As Byte
  1046. Array.Copy(bytes, 0, tmp, 0, chunkToBreakOff)
  1047. GetSome = tmp
  1048. If bytes.Length = chunkToBreakOff Then
  1049. bytesToBeSentAvailable = False
  1050. If theseAreUserBytes Then SystemMessage("UBS:" & session.sessionID & ":" & session.UserOutputChannel)
  1051. Else
  1052. Dim tmp2(bytes.Length - chunkToBreakOff - 1) As Byte
  1053. Array.Copy(bytes, chunkToBreakOff, tmp2, 0, bytes.Length - chunkToBreakOff)
  1054. bytes = tmp2
  1055. End If
  1056. End Function
  1057. Private Sub SystemMessage(ByVal MsgText As String)
  1058. Dim bgMsg As New Thread(AddressOf BgMessage)
  1059. bgMsg.IsBackground = True
  1060. bgMsg.Start(MsgText)
  1061. End Sub
  1062. Private Sub BgMessage(ByVal _text As Object)
  1063. Dim msg As String = CType(_text, String)
  1064. RcvBytes(Utilities.StrToByteArray(msg), Nothing, 255)
  1065. End Sub
  1066. 'Private Sub SystemMessage(ByVal MsgText As String, ByRef session As SessionCommunications)
  1067. ' RcvBytes(Utilities.StrToByteArray(MsgText), session, 255)
  1068. 'End Sub
  1069. ' Check to see if our app is closing (set in FormClosing event)
  1070. Private Function theServerIsStopping(ByVal Server As TcpClient, ByVal session As SessionCommunications) As Boolean
  1071. If Not continue_running Or session.disConnect Then
  1072. theServerIsStopping = True
  1073. Else
  1074. theServerIsStopping = False
  1075. End If
  1076. End Function
  1077. Private Sub theListener()
  1078. Try
  1079. ' Start listening
  1080. SystemMessage("Listening...")
  1081. Listener = New TcpListener(localAddr, Port)
  1082. Listener.Start()
  1083. StartAccept()
  1084. Catch ex As Exception
  1085. errMsg = ex.Message
  1086. serverState = currentState.err
  1087. Exit Sub
  1088. End Try
  1089. serverState = currentState.running
  1090. End Sub
  1091. Private Function StartAccept() As Boolean
  1092. ' Manage the rate at which
  1093. ' we accept new connections.
  1094. Thread.Sleep(10)
  1095. Try
  1096. Listener.BeginAcceptTcpClient(AddressOf HandleAsyncConnection, Listener)
  1097. Return True
  1098. Catch ex As Exception
  1099. Return False
  1100. End Try
  1101. End Function
  1102. Private Sub HandleAsyncConnection(ByVal res As IAsyncResult)
  1103. If Not StartAccept() Then Exit Sub
  1104. Try
  1105. SessionCollection.AddSession(Listener.EndAcceptTcpClient(res), New Thread(AddressOf Run), enforceUniqueMachineIds)
  1106. GC.GetTotalMemory(True)
  1107. Catch ex As Exception
  1108. End Try
  1109. End Sub
  1110. Private Sub WrapUpIncomingFile(ByVal session As SessionCommunications)
  1111. If session.ReceivingFile Then
  1112. Try
  1113. session.fileWriter.Close()
  1114. session.fileWriter = Nothing
  1115. GC.GetTotalMemory(True)
  1116. Catch ex As Exception
  1117. End Try
  1118. Try
  1119. File.Delete(session.ReceivedFilesFolder & "\" & session.IncomingFileName)
  1120. Catch ex As Exception
  1121. End Try
  1122. End If
  1123. End Sub
  1124. Private Sub Run(ByVal _session As Object)
  1125. Dim session As SessionCommunications = DirectCast(_session, SessionCommunications)
  1126. session.sendQueue = New ConcurrentQueue(Of message)
  1127. session.messageIn = New SessionCommunications.MessageInQueue(ServerCallbackObject)
  1128. Dim Server As TcpClient
  1129. Dim Stream As NetworkStream
  1130. Dim IpEndPoint As IPEndPoint
  1131. Dim puck(1) As Byte : puck(0) = 0
  1132. Dim theBuffer(blockSize - 1) As Byte
  1133. Dim tmp(1) As Byte
  1134. Dim dataChannel As Byte = 0
  1135. Dim packetSize As UShort = 0
  1136. Dim idleTimer, bandwidthTimer As Date
  1137. Dim bytesread As Integer = 0
  1138. Dim weHaveThePuck As Boolean = True
  1139. Dim bandwidthUsedThisSecond As Int32 = 0
  1140. Dim userOrSystemSwitcher As Integer = 0
  1141. Dim packetReceiveTimeout As Date
  1142. session.disConnect = False
  1143. Try
  1144. ' Create a local Server and Stream objects for clarity.
  1145. Server = session.theClient
  1146. Stream = Server.GetStream()
  1147. Catch ex As Exception
  1148. ' An unexpected error.
  1149. Debug.WriteLine("Could not create local Server or Stream object in server. Message: " & ex.Message)
  1150. Exit Sub
  1151. End Try
  1152. Stream.ReadTimeout = 5000
  1153. Try
  1154. ' Get the remote machine's IP address.
  1155. IpEndPoint = CType(Server.Client.RemoteEndPoint, Net.IPEndPoint)
  1156. session.remoteIpAddress = IpEndPoint.Address
  1157. ' Set the send and receive buffers to the maximum
  1158. ' size allowable in this application...
  1159. Server.Client.ReceiveBufferSize = 65535
  1160. Server.Client.SendBufferSize = 65535
  1161. ' no delay on partially filled packets...
  1162. ' Send it all as fast as possible.
  1163. Server.NoDelay = True
  1164. ' Set the timers...
  1165. idleTimer = Now
  1166. 'bandwidthTimer = Now
  1167. bandwidthTimer = Now.AddMilliseconds(250)
  1168. session.IsRunning = True
  1169. SystemMessage("Connected.")
  1170. ' Start the communication loop
  1171. Do
  1172. ' Throttle network Mbps...
  1173. bandwidthUsedThisSecond = session.bytesSentThisSecond + session.bytesRecievedThisSecond
  1174. If bandwidthTimer >= Now And bandwidthUsedThisSecond >= (Mbps / 4) Then
  1175. While bandwidthTimer > Now
  1176. Thread.Sleep(1)
  1177. End While
  1178. End If
  1179. If bandwidthTimer <= Now Then
  1180. bandwidthTimer = Now.AddMilliseconds(250)
  1181. session.bytesRecievedThisSecond = 0
  1182. session.bytesSentThisSecond = 0
  1183. bandwidthUsedThisSecond = 0
  1184. End If
  1185. ' Normal communications...
  1186. If weHaveThePuck Then
  1187. ' Send data if there is any to be sent...
  1188. userOrSystemSwitcher += 1
  1189. Select Case userOrSystemSwitcher
  1190. Case 1
  1191. If Not session.paused Then
  1192. If HandleOutgoingUserData(Stream, session) Then idleTimer = Now
  1193. End If
  1194. Case 2
  1195. If HandleOutgoingInternalSystemMessage(Stream, session) Then idleTimer = Now
  1196. End Select
  1197. If userOrSystemSwitcher > 1 Then userOrSystemSwitcher = 0
  1198. ' After sending out data, send the puck
  1199. Stream.Write(puck, 0, 1)
  1200. weHaveThePuck = False
  1201. End If
  1202. If theBuffer.Length < 2 Then ReDim theBuffer(1)
  1203. ' Read in the control byte.
  1204. Stream.Read(theBuffer, 0, 1)
  1205. dataChannel = theBuffer(0)
  1206. ' If it's just the puck (communictaion syncronization byte),
  1207. ' set weHaveThePuck true, record the byte read for throttling,
  1208. ' and that's all. dataChannel 0 is reserved for the puck.
  1209. If dataChannel = 0 Then
  1210. weHaveThePuck = True
  1211. session.bytesRecievedThisSecond += 1
  1212. Else
  1213. ' It's not the puck: It's an incoming packet.
  1214. ' Get the packet size:
  1215. tmp(0) = Convert.ToByte(Stream.ReadByte)
  1216. tmp(1) = Convert.ToByte(Stream.ReadByte)
  1217. packetSize = BitConverter.ToUInt16(tmp, 0)
  1218. session.bytesRecievedThisSecond += 2
  1219. ' Get the packet:
  1220. If theBuffer.Length <> packetSize Then ReDim theBuffer(packetSize - 1)
  1221. packetReceiveTimeout = Now.AddSeconds(3)
  1222. Do
  1223. ' Read bytes in...
  1224. bytesread += Stream.Read(theBuffer, bytesread, (packetSize - bytesread))
  1225. ' We've been waiting for moew data for 3 seconds... we've lost connection.
  1226. If packetReceiveTimeout < Now Then Throw New Exception("Timeout waiting for data from client. Connection lost.")
  1227. Loop While bytesread < packetSize
  1228. bytesread = 0
  1229. ' Record bytes read for throttling...
  1230. session.bytesRecievedThisSecond += packetSize
  1231. ' Handle the packet...
  1232. If dataChannel > 250 Then
  1233. ' this is an internal system packet
  1234. If Not theServerIsStopping(Server, session) Then HandleIncomingSystemMessages(theBuffer, dataChannel, session)
  1235. Else
  1236. ' Hand user data off to the calling thread.
  1237. If Not theServerIsStopping(Server, session) Then RcvBytes(theBuffer, session, dataChannel)
  1238. End If
  1239. idleTimer = Now
  1240. End If
  1241. ' Throttle CPU usage when idle.
  1242. If Now > idleTimer.AddMilliseconds(500) Then
  1243. Thread.Sleep(50)
  1244. End If
  1245. Loop
  1246. Catch ex As Exception
  1247. ' An unexpected error.
  1248. If ex IsNot Nothing Then Debug.WriteLine("Unexpected error in server: " & ex.Message)
  1249. End Try
  1250. session.machineIdValidated = False
  1251. session.machineId = ""
  1252. Try
  1253. If session.fileReader IsNot Nothing Then session.fileReader.Close()
  1254. Catch ex As Exception
  1255. End Try
  1256. Try
  1257. Server.Client.Blocking = False
  1258. Server.Client.Close()
  1259. Catch ex As Exception
  1260. End Try
  1261. ' If we're in the middle of receiving a file,
  1262. ' close the filestream, release the memory and
  1263. ' delete the partial file.
  1264. WrapUpIncomingFile(session)
  1265. If session.disConnect = False Then
  1266. SystemMessage("Session Stopped. (" & session.sessionID.ToString & ")")
  1267. SessionCollection.CloseSession(session)
  1268. End If
  1269. session.messageIn.Close()
  1270. End Sub
  1271. End Class