65.9K
CodeProject 正在变化。 阅读更多。
Home

多套接字编程的综合分析 - 客户端

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.80/5 (4投票s)

2007年10月2日

5分钟阅读

viewsIcon

26438

本文包含多套接字编程(第一部分)- 客户端的综合分析。

引言

本文对客户端套接字编程进行了概要分析。在套接字编程中进行客户端编码与在多套接字编程中进行客户端编码是不同的。


背景

多套接字编程比传统套接字编程更进一步。在传统套接字编程中,一个客户端连接到一个在特定端口监听的服务器;而在多套接字编程中,一个客户端连接到同一台机器或不同机器上的不同端口的两个服务器。

本次分析基于 **Microsoft Visual Studio 2003 和 .Net Framework 1.1**。本次分析纯粹面向 Microsoft Visual Studio 2003 和 .Net Framework 1.1 的中高级或专家级专业人士。如果您的系统开启了防火墙,请确保它不会阻止连接。

在本报告中,我添加了这些应用程序的**顺序分析和流程分析**;这将极大地增强您的知识。对于软件工程领域的专业人士来说,理解执行顺序和流程至关重要。如果您认为自己在软件开发方面是专家,可以直接跳到这些(顺序分析和流程分析)部分进行学习。

有些专业人士可能会觉得从技术角度的解释范围有限,但对于充分理解这些应用程序(即服务器和客户端)来说已经足够了。如果我写得面面俱到,那将是一段漫长的旅程,而且可能会让您感到困惑。请记住,解释的范围应根据需求而定,在此我们已达到我们需求和目的的限制。即使您想进一步探索,我也欢迎。

在文末,我包含了 **完整的参考资料**(即 TcpListener 类、TcpClient 类的定义、概述、公共构造函数、公共方法、保护方法、属性等)。请务必阅读它们,因为我在多套接字编程中使用了 TcpListener 类和 TcpClient 类。

使用代码

多套接字编程

什么是多套接字编程?从技术上讲,它是一种套接字编程方式,其中两个套接字连接到两个不同的服务器或同一个服务器。多套接字编程究竟是什么?为此,请同时了解传统套接字编程与多套接字编程。

套接字是用于连接计算机的逻辑连接。创建套接字需要一个端口号和一个主机 IP 地址/主机名。线程是进程或程序中被分配了某些资源(如文件、I/O 等)并且能够独立运行的部分。多线程意味着一个进程中的许多线程可以在单个处理器上运行,它们被分配相等的执行时间片和优先级,每个线程都感觉自己是唯一运行的进程。

当我们有需要在两台机器(此处“机器”指计算机系统)之间进行通信时,我们就会实现套接字编程,其中一台机器充当服务器,而另一台充当客户端。

服务器的角色是在特定端口上监听传入的连接请求。客户端通过服务器的 IP 地址(或服务器名称)以及服务器正在监听的端口号向服务器发送请求以建立通信。现在,在多套接字编程中,客户端会向在不同端口上监听的两个或多个服务器发送连接请求。

服务器是否接受(客户端发送的)连接请求,还是拒绝它,这取决于服务器。如果服务器接受连接,则会创建一个虚拟管道(技术上称为套接字),用于服务器和客户端之间的双向消息通信。当客户端与服务器有活动连接时,它可以与服务器发送和接收数据,然后关闭连接。这些连接在资源利用方面成本较高。服务器还可以限制连接的客户端数量。我再次强调,这是客户端/服务器架构,因为一台计算机将充当服务器(响应客户端消息),而所有其他计算机则充当客户端(仅向服务器发送通信建立请求)。

多套接字编程展现出的一个美妙的复杂性是,服务器向任何已连接的客户端或所有已连接的客户端(即广播)发送数据,这些数据需要由客户端读取,但客户端如何知道是哪个服务器发送了这些数据?另一个困难是,当任何一方向另一方发送数据时,对方应该读取它,但对方如何知道有数据到达?我将在本文档的后面部分详细解释这些问题,请继续阅读。

代码分析 - 客户端

在这里,我将探讨一个简单的客户端,以便更好地理解。这个客户端连接到两个不同的服务器并开始与它们通信。使用的函数总数如下所示:

i) Form2_Load()
ii) DoReadOne()
iii) DoReadSec()
iv) DisplayText()
v) btnSendFirst_Click()
vi) SendForFirst()
vii) btnSendSecond_Click()
viii) SendForSecond()
ix) btnAllServer_Click()
x) MarkAsDisconnected()

我在我的每份文档中都会写下重要的一点,那就是在继续之前,所有这些函数都是按结构列出的。如果有人试图查找语法错误(例如,Form2_Load 事件的参数数量),请原谅我。感谢上帝。

现在,再多说一句,以下是此项目类所使用的导入文件:
Imports System.Net
Imports System.Net.Sockets

这两份文件都很重要,因为 .net 通过这些类提供了丰富的功能集。

'declaring form level variables 

Private marDataOne(1024) As Byte 
Private mobjClient, mobjClientSec As TcpClient 
Private Delegate Sub callDisplayText(ByVal t As String) 

'marDataOne' is an array of bytes which is used to store data which is coming from server in 'DoReadOne()' function.

'mobjClient' & 'mobjClientsec' are objects of TcpClient class & are used to connect to particular servers at concern port see in 'Form2_Load()'.

Elaborating Function 'Form2_Load()'

The full code is as under

"vb.net">'code on form load event 

Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As 
System.EventArgs) Handles MyBase.Load 
Try 
'create a new client & display message 

 mobjClient = New TcpClient("192.168.1.8", 1007) 
DisplayText("Connected To Server At Port: 1007" & "~" & "DoReadOne") 
'code to read data using stream 

mobjClient.GetStream.BeginRead(marDataOne, 0, 1024, AddressOf DoReadOne, Nothing) 
'create a new client & display message 

mobjClientSec = New TcpClient("192.168.1.8", 2007) 
DisplayText("Connected To Server At Port: 2007" & "~" & "DoReadSec") 
'code to read data using stream 

mobjClientSec.GetStream.BeginRead(marDataOne, 0, 1024, AddressOf DoReadSec, Nothing) 
Catch ex As Exception 
'declaring error string variable & assinging error message 

Dim strErrMsg As String 
strErrMsg = ex.Message.ToString 
'checking for particular error message 

If strErrMsg = "No connection could be made because the target machine actively refused it" Then 
strErrMsg = "Ask Administrator To Start The Server As " & vbCr & vbCr & "No Connection Could Be Made Because The Target Machine Actively Refused It" 
MessageBox.Show(strErrMsg, "Connection Error") 
Else 
strErrMsg = "Error: While Connecting To Desired Machine For Communication" 
MessageBox.Show(strErrMsg, "Connection Error") 
End If 
'exiting from application properly 

Application.Exit() 
Me.Close() 
End Try 
End Sub 

"TEXT-ALIGN: justify">In this function objects of TcpClient 'mobjClient' & 'mobjClientSec' is sending a request to server for communication by passing Ipaddress and port of machine. DisplayText() function is used to display the data on listbox.

Now I am checking asynchronously for data to receive from either server. This is done with the help of BeginRead() function which is available in getstream of TcpClient object. This BeginRead function is called twice first by 'mobjClient' and second by 'mobjClientSec'. All the expected generic errors are first identify and handled carefully. The code in catch block is self explanatory.

Elaborating Function 'DoReadOne()'

"TEXT-ALIGN: justify">DoReadOne() function accepts an object of IAsyncResult interface. This object is responsible for receiving no of bytes from network stream. If number of bytes received are less than 1 it means that particular socket is now closed (obviously it means that particular server is closed) so call 'MarkAsDisconneced()' function to exit the application successfully. If number of bytes are greater than one it means some data has arrived now to get that data use = 'System.Text.ASCIIEncoding' property. Finally invoking DisplayText() function by delegate to display data on list box. The catch block is again self explanatory. Do remember that reading and displaying of data in DoReadone is done by (only by) mobjClient.

'function to listen port asynchronsly to read data from server 

Private Sub DoReadOne(ByVal ar As IAsyncResult) 
Try 
'declaring some local variables & assinging values 

Dim strMsg As String 
Dim intCount As Integer 
Dim tempObj As Object 
'getting no of bytes available to read 

intCount = mobjClient.GetStream.EndRead(ar) 
'checking if connected or disconnected 

If intCount < 1 Then 
'call markAsDisconected() function 

MarkAsDisconnected() 
Exit Sub 
End If 
'GETTING STRING IN ONE LINE 

strMsg = System.Text.ASCIIEncoding.ASCII.GetString(marDataOne, 0, intCount) 
strMsg = strMsg & "~" & "DoReadOne" 
'calling displayText() thru delegate 

Dim obj As New callDisplayText(AddressOf DisplayText) 
obj.BeginInvoke(strMsg, Nothing, Nothing) 
'read data from port 

mobjClient.GetStream.BeginRead(marDataOne, 0, 1024, AddressOf DoReadOne, Nothing) 
Catch e As Exception 
MarkAsDisconnected() 
End Try 
End Sub 

"TEXT-ALIGN: justify">Look at this code "System.Text.ASCIIEncoding.ASCII.GetString(marDataOne, 0, intCount)

TEXT-ALIGN: justify">GetString function accepts three arguments first is the byte array, second is index, third is number of bytes to read.

TEXT-ALIGN: justify">BeginRead() function accepts five parameters

TEXT-ALIGN: justify">First: byte array

TEXT-ALIGN: justify">Second: offset

TEXT-ALIGN: justify">Third: size i.e. number of bytes to be read

TEXT-ALIGN: justify">Fourth: a callback delegate

TEXT-ALIGN: justify">Fifth: object

TEXT-ALIGN: justify">Here this last i.e. fifth parameter in BeginRead() function is set to nothing but this can be used to retrieve additional information of socket. After executing BeginRead() function I again invoke BeginRead() function just to make sure that whenever there is data available to socket it can be retrieved instantly.

Elaborating Function 'DoReadSec()'

Whole thing is same in this function as they are in DoReadOne() except it is meant for second TcpClient class's object mobjClientSec() that is to communicate with second server.

Elaborating Function 'DisplayText ()'

This function is used to show data on two different list box depending upon from which server it is coming. That is if it is coming from first server this will show data on first list box else in second. This categorization is done with the help of incoming data as a parameter. As this data is concatenated with identifier "DoReadOne" and "DoReadSec" in DoReadOne() and in DoReadSec() function respectively.

'function to display message in text box 
Private Sub DisplayText(ByVal t As String) 
Dim strMsg As String 
Dim arrMsg(2) As String 
arrMsg = t.Split("~") 
strMsg = arrMsg(0) 
If arrMsg(1) = "DoReadOne" Then 
lstBoxOne.Items.Add(strMsg) 
Else 
lstBoxSec.Items.Add(strMsg) 
End If 
End Sub 

Elaborating Function 'btnSendFirst_Click()'

TEXT-ALIGN: justify">This block of code is invoked whenever user click ToFirst button from GUI. This code take data from text box and call sendForFirst() function to send data to First server. After sending data to server text box is cleared. The complete code is as under

'code to send message to first server 
Private Sub btnSendFirst_Click(ByVal sender As System.Object, ByVal e As 
System.EventArgs) Handles btnSendFirst.Click 
Try 
'validation to enter some text to send 
If txtSend.Text = "" Then 
MessageBox.Show("Please Enter Some Text In Message Box Before Sending") 
Exit Sub 
End If 
'calling send function to send data to server 
SendForFirst(txtSend.Text) 
txtSend.Text = "" 
Catch ex As Exception 
MessageBox.Show(ex.ToString) 
End Try 
End Sub 

Elaborating Function 'SendForFirst ()'

TEXT-ALIGN: justify">Send function is responsible to send data to network stream to first server. This is achieved with the help of object of IO.StreamWriter class. This function gets invoked whenever ToFirst button is clicked. Use of flush() is to clears all buffers for current writer. The complete code goes like this

'function to send data to first server 
Private Sub SendForFirst(ByVal t As String) 
Try 
Dim w As New IO.StreamWriter(mobjClient.GetStream) 
w.Write(t) 
w.Flush() 
Catch ex As Exception 
MessageBox.Show(ex.ToString) 
End Try 
End Sub 

Elaborating Function 'btnSendSecond_Click()'

Same conceptual theory as btnSendFirst_Click() except it is meant for sending data to second server.

Elaborating Function 'SendForSecond ()'

Same conceptual theory as SendForFirst except it is meant for sending data to second server.

Elaborating Function 'btnAllServer_Click()'

This function is invoked wherever client click on ToAllServer button. This is used to send data of text box to both the servers. The code is self explanatory as it invoke sendForFirst() & sendForSecond() function to send data to both servers.

'code to send message to all servers 
Private Sub btnAllServer_Click(ByVal sender As System.Object, ByVal e As 
System.EventArgs) Handles btnAllServer.Click 
Try 
'validation to enter some text to send 
If txtSend.Text = "" Then 
MessageBox.Show("Please Enter Some Text In Message Box Before Sending") 
Exit Sub 
End If 
'calling send function to send data to all servers 
SendForFirst(txtSend.Text) 
SendForSecond(txtSend.Text) 
txtSend.Text = "" 
Catch ex As Exception 
MessageBox.Show(ex.ToString) 
End Try 
End Sub 

Elaborating Function 'MarkAsDisconnected()'

TEXT-ALIGN: justify">This function is simply invoked in any case when there is a need to exit the application uninterruptedly. The complete code goes like this

'function to mark connection as disconnected 
Private Sub MarkAsDisconnected() 
Try 
'declare string variable for displaying error message 
Dim strErrMsg As String 
'concatenating error string msg 
strErrMsg = "Unable To Communicate With Server, Server Is Not Responding" 
MessageBox.Show(strErrMsg, "Connection Error") 
'exiting from application properly 
Me.Close() 
Application.Exit() 
Catch ex As Exception 
MessageBox.Show(ex.ToString) 
End Try 
End Sub 

Order Execution Analysis– Client
As sound professional of software engineering one should know the order of execution of application. One should know how function is invoked how they call each other and how whole process came to end for a particular process in application. That is why I am doing with Client application.

Following are the categories that I will explore in terms of their order of execution for client

i) when client starts
ii) when message send to first server
iii) when message send to second server
iv) when message send to all server
v) when message receive from first server
vi) when message receive from second server
vii) when first server close
viii) when second server close
ix) when client close

i) When client starts

form_load() calls DoReadSec() and DoReadOne() asynchronously. which in turn DisplayText function is called by both.

ii) when message send to first server

This called sendForFirst() function.

iii) when message send to second server

This called sendForSecond() function.

iv) when message send to all server

This called sendForFirst() & sendForSecond() function.

v) when message receive from first server

This calls first for DoReadOne() and then DisplayText() function.

vi) when message receive from second server

This calls first for DoReadSec() and then DisplayText() function.

vii) when first server close

This call DoReadOne() which in turn call DoReadOne() and finally MarkAsDisconnected() functions.

vii) when second server close

This call DoReadSec() which in turn call DoReadSec() and finally MarkAsDisconnected() functions.

vii) when client close

Nothing happens.

© . All rights reserved.