Multithreaded Winsock
Vb6 cho ta Winsock Control để giúp mt program VB6 nói chuyn vi mt
program khác trên mng TCP/IP.
Ta có th dùng Winsock Control trong mt program để làm Winsock Server hay
Winsock Client. S khác bit ny rt nh, mc du ta phi lưu ý để phân bit s
khác nhau ca hai trường hp. Gi s ta dùng Winsock Control làm Server trong
mt VB6 program để chy trên mt computer và dùng Winsock Control làm
Client trong mt VB6 program để chy trên mt computer khác trên mng
TCP/IP. Ð cho hai programs nói chuyn (communicate) trước hết ta cn phi
connect (ni) chúng li vi nhau.
Ta cho Winsock Server Listen (lng nghe) qua mt LocalPort (mt cng có
mang mt con s, thí d như 9123). Kế đó ta cho Winsock Client Connect (móc
ni) qua LocalPort đó địa ch TCP ca Computer nơi ta chy Winsock Server
program. S d ta cn phi nói rõ LocalPort s my là vì Server Computer có th
Listen qua nhiu LocalPorts cùng mt lúc để nhiu Clients có th Connect đến
cùng mt Computer TCP address. (Nếu bn còn mi đối vi TCP/IP hãy đọc bài
Căn bn TCP/IP )
Class ServerWinsock và Class ClientWinsock
Trong .NET, Winsock được thay thế bng TcpListenerTcpClient ca
System.Net.Sockets. Để dùng chúng ta ch cn Project | Add Reference..
cái System.dll và thêm câu:
Imports System.Net.Sockets ' for TcpClient and TcpServer
đầu phn code.
Khi instantiate mt TcpListener object, ta cho nó mt PortNo để nó lng nghe
qua cng đó như sau:
Dim oListener As TcpListener ' Variable for TcpListener
' Instantiate a TcpListener on given PortNo
oListener = New TcpListener(PortNo)
oListener.Start() ' Start the TcpListener
V phía Client, ta gi method Connect ca TcpClient vi tên ca
destination/server computer (hay TCP address ca computer y) và cái cng trên
destination/server computer. Ta code như sau:
Dim Client As TcpClient ' Variable for the Client TCP socket
' Instantiate TCPClient object
Client = New TcpClient()
' Attempt to connect to destination (server) computer on given port
number
Client.Connect(DestinationComputer, TCPIPPortNo)
Bên TcpListener s dùng mt Socket để Accept (nhn) cái Request (thnh cu)
ca TcpClient:
' Accept request from the TcpClient
Dim oSocket As Socket
oSocket = oListener.AcceptSocket
Khi TcpListener AcceptSocket ri thì hai bên TcpClient và TcpListener có th gi
thông đip qua li cho đến khi mt bên terminates (stop).
Dưới đây là hình minh ha s móc ni và gi thông đip t Client (máy SAIGON)
qua Server (máy SADEC). T Server ta cũng có th gi thông đip qua Client
cùng mt cách như vy.
Mt khi connection đã đứt đon, không d cho ta ni li. Trên nguyên tc, hai
bên phi đóng socket ri tìm cách lng nghe/móc ni tr li.
.NET cho ta mt gii pháp đơn gin và thanh tao, đó là dùng thread, mt dng
process nh ký. cùng mt cng, mi khi nhn được Request-to-connect t mt
TcpClient, ta instantiate mt Socket chy trong mt thread riêng để phc v
TcpClient y. Khi TcpClient disconnects thì ta cũng đóng socket ny.
Bên phía TcpClient, mi ln cn gi mt thông đip ta instantiate mt TcpClient
mi, và sau khi gi xong ta disconnect nó ngay.
Cách dùng thread rt đơn gin. Mun mt Sub chy riêng trong mt thread ta
ch cn instantiate mt thread vi AddressOf ca Sub y, ri khi động thread
y như sau:
' create a thread to handle this Client Request
Dim oThread As Thread
oThread = New Thread(AddressOf ProcessRequest)
oThread.Start() ' Run Sub ProcessRequest
Để dùng Thread ta ch cn thêm câu:
Imports System.Threading ' for Thread
đầu phn code.
Trong d án ny, TcpListener được gói trong class ServerWinsock
TcpClient được gói trong class ClientWinsock. Chính bên trong class
ServerWinsock ta dùng multithread để phc v nhiu TcpClient qua cùng mt
cng TCPPortNo duy nht.
Class ClientWinsock ch gi thông đip và class ServerWinsock ch nhn thông
đip. Khi ServerWinsock nhn mt thông đip nó s Raise mt Event để
program ch ca nó x lý thông đip. Thông đip được gi đi li dưới dng mt
array of bytes. Do đó mun gi mt Text String ta phi cho biết Encode ca Text
string lúc by gi UTF8, Unicode hay ASCII, và đổi nó ra array of bytes như
sau:
Dim Buffer() As Byte ' used for outgoing message
' Convert UFT8 message to an array of bytes before sending
Buffer = System.Text.Encoding.UTF8.GetBytes(mMessage.ToCharArray)
' Send out the buffer
Client.GetStream().Write(Buffer, 0, Buffer.Length)
V phía đầu ServerWinsock, khi nhn được array of bytes thì phi đổi ra Text
string tr li như sau:
' Convert the array of bytes (i.e. the buffer) to UTF8 text string
RecvMessage = System.Text.Encoding.UTF8.GetString(Buffer)
' Raise an event to return the message to the program that owns this
ServerWinsock
RaiseEvent OnMessage(RecvMessage)
Trong thí d ny ta dùng UTF8 để gi Unicode. Nếu d kin ch là ASCII thì có
th dùng encoding ASCII cho hiu lc hơn vì mi ASCII character ch cn mt
byte:
Buffer = System.Text.Encoding.ASCII.GetBytes(mMessage.ToCharArray) '
Chun b Buffer để gi đi
RecvMessage = System.Text.Encoding.ASCII.GetString(Buffer) ' Đởi li
thành ASCII text string khi nhn
Tht ra để gi Unicode ta cũng có th dùng encoding Unicode, tc là UTF16
LittleEndian. (Nếu bn còn mi đối vi Unicode encoding hãy đọc bài Dùng
Unicode ch Vit trong .NET).
Dưới đây là mã ngun ca hai classes ClientWinsock và ServerWinsock:
Imports System.Threading ' for threads
Imports System.Net.Sockets ' for TcpClient and TcpServer
' This module contains two classes: ClientWinsock and ServerWinsock
Public Class ClientWinsock
' This object is created to connect to a TCPServer and to send a
single Unicode message
Private ClientThread As Thread ' used to run the main Sub
StartClient of Client
Private TCPIPPortNo As Integer ' TCPIP port number on destination
computer
Private DestinationComputer As String ' name or IP address of
destination computer
Private mMessage As String ' Unicode message to be sent
Public Sub New( ByVal Destination As String, ByVal Message As
String)
' Split the given Message into Destination computer and TCPIP port
number
Dim pos As Integer
' Locate the character ";" in the Message string
pos = Destination.IndexOf(";")
If pos > 0 Then
' the part before ";" is the name or IP address of destination
computer
DestinationComputer = Destination.Substring(0, pos)
TCPIPPortNo = CInt(Destination.Substring(pos + 1)) ' convert
string to integer
Else
' character ";" does not exist, that means only TCPIP Port
number is given for Localhost
DestinationComputer = "Localhost" ' Destination computer is
Localhost
TCPIPPortNo = CInt(Destination) ' convert string to integer
End If
mMessage = Message ' assign outgoing message to local string
variable
'Create a Thread object for Sub StartClient
ClientThread = New Thread(AddressOf StartClient)
'Starting the thread invokes the ThreadStart delegate
' i.e. run Sub StartClient in its own thread
ClientThread.Start()
End Sub
Protected Sub StartClient()
' This is the main code in ClientWinsock. It's run in its own
thread
Dim Client As TcpClient ' Variable for the Client TCP socket
Dim Buffer() As Byte ' used for outgoing message
Try
' Instantiate TCPClient object
Client = New TcpClient()
' Attempt to connect to destination (server) computer on given
port number
Client.Connect(DestinationComputer, TCPIPPortNo)
' Convert UFT8 message to an array of bytes before sending
Buffer =
System.Text.Encoding.UTF8.GetBytes(mMessage.ToCharArray)
' Send out the buffer
Client.GetStream().Write(Buffer, 0, Buffer.Length)
Client.Close() ' Close the TcpClient
Catch e As Exception
' Write to Console the message that cannot be sent
Console.WriteLine("Can 't send:" & mMessage)
Finally
ClientThread.Abort() ' Abort thread
End Try
End Sub
End Class
Public Class ServerWinsock
' This object is created to serve many TCPClients and to receive
Unicode messages
' A thread is created to serve each TCPClient
Const MaxThread As Integer = 500 ' Maximum number of threads that
ServerWinsock can handle
Private oListener As TcpListener ' Variable for TcpListener
Private bStopListener As Boolean ' Flag indicating that user wants
to dispose this ServerWinsock
Private ActiveThreads As Integer ' Number of active threads, i.e.
threads that are serving TCPClients
' Event that returns the incoming message
Public Event OnMessage( ByVal IncomingMessage As String)
Public Sub New( ByVal PortNo As Integer)
' Instantiate a TcpListener on given PortNo
oListener = New TcpListener(PortNo)
oListener.Start() ' Start the TcpListener
' Create a thread for Sub AcceptConnection
Dim ServerThread As Thread
ServerThread = New Thread(AddressOf AcceptConnection)
ServerThread.Start() ' Run Sub AcceptConnection like a light
weight child process
End Sub
Protected Sub ProcessRequest()
Dim Buffer(5000) As Byte ' used to receive incoming message from
TcpClient
Dim bytes As Integer ' Actual number of bytes read
Dim RecvMessage As String ' UTF8 text string of the buffer (array
of bytes)
' Use oThread to reference the thread of this Sub
Dim oThread As Thread
oThread = Thread.CurrentThread()
' Accept request from the TcpClient
Dim oSocket As Socket
oSocket = oListener.AcceptSocket