随着Internet 爆炸式的发展及Windows用户不断地增多,人们迫切地需要一种在Windows下开发TCP/IP应用程序标准,由包括微软公司在内的众多计算机厂家,经共同努力,已经制订出了这一标准,称之为Windows Sockets API。
作者:论坛管理 来源:zdnet安全频道 2009年1月7日
关键字: TCP/IP
随着Internet 爆炸式的发展及Windows用户不断地增多,人们迫切地需要一种在Windows下开发TCP/IP应用程序标准,由包括微软公司在内的众多计算机厂家,经共同努力,已经制订出了这一标准,称之为Windows Sockets API(application Program interface)。这使得不同厂家开发的应用程序能够做到相互兼容。
一、TCP/IP编译简介:
当两台计算机通过网络要进行信息交换时,需要具备两个条件:一是物理配件,这包括网卡及连接网卡用的网线,二是需要一组通讯参数的说明,即协议。目前最广泛使用的协议是TCP/IP协议。
当一个主机使用IP协议发送数据时,数据被分为数据包.每个数据包由其包头及数据组成,包头包含对方目的地址。这就象使用信封发信一样,信封上含有收方的地址,但有时发出的信也会丢掉,这种发送称为不可靠的传输,而我们需要的是可靠的传输,这便产生了TCP协议。
TCP是一种面向连接的协议,即:两个程序在进行数据交换之前,他们必须先建立起连接,一个程序作为客户方(client)发出连接请求,另一个程序作为服务方(server)监听,并响应其连接请求,一旦连接建立好,双方便均可收发信息,直到连接断开。TCP协议使得开发人员不需要去编写如何处理数据包丢失的过程,而专心于应用程序本身的开发。
为了同其他计算机进行通信,还需要知道本机及目的机的IP地址,有时为方便记忆我们将32位的IP地址用主机名来代替,主机名间用"·"分隔,我们称之为域,域是一种树形结构。如: 最上一层为政府、商业公司、教育机构、internet服务商等组织,一个完整的域名是由主机及其所有的父名组成(用注释:·注释:分隔),例如mars的完整域名为mars·olgmpus·com,其表达的含意为mars是olgmpus域名的一部分,而olgmpus又是com的一部分。IP地址到主机名的转换有两种方法,一是使用本地的主机命名表文件,这个文件通常称为hosts文件,表中列出了IP地址及主机名的对应关系;二是使用命名服务器(DNS),它是一台主机(或一个应用程序),可以将一主机名转换为其IP地址。
此外,除了双方的IP地址以外,还需要知道对方的服务端口号(Service Port),它是一个16位的标识,每一Service Port同一应用程序相对应,这些对应关系往往存在于名为Service的一个文件中,一些常用的服务及对应的
Service Port如下:
Service Servicse Port
FTP 21
Telnet 23
SMTP 25
DNS 53
TFTP 69
SNMP 161
TCP 6
UDP 17
有了以上各信息,两应用程序在进行通讯时,先建立一个Socket(或称为通讯端点),建立了Socket本身并不能进行信息交换,你还必须建立Socket的连接,Socket的地址由三部分组成:协议、IP地址、Service Port号。其协议标识着下一层所使用何种协议,在我们以下的例子中就是指IP协议。
在两个应用程序进行通讯时,客户方建立一个Socket并试图同服务方建立连接,服务方也建立一个Socket等待客户方发来的连接请求,当收到一个申请后,双方便形成一条虚电路(即两个程序之间的一条逻辑通讯链路)。在此强调一下:当服务方收到连接请求后,服务方建立一新的Socket,用此新的Socket同对方建立连接,原来的Socket保持不变,可继续等待其它连接请求,当服务方不再希望收到其它连接时,它将最早的Socket关闭。
在建立一个TCP通讯程序时,服务方的程序应完成以下五个步骤:
1、建立一个Socket
2、监听从客户方发来的连接请求
3、接受客户方的连接
4、开始收、发信息
5、关闭Socket,终结会话
在客户方应完成以下五个步骤:
1、建立一个Socket
2、指定服务方IP地址及Service Port
3、同服务方建立连接
4、开始收、发信息
5、关闭Socket,终结会话
由上可以看出服务方和客户方的第二步和第三步是不同的,另外,在开发应用程序时,你可能会遇到阻塞式和非阻塞式Socket。例如:当你从Socket读数据时,如果远方主机还未将数据传来,你就读不到数据,这会引发两种情况:一是程序一直等待,直到数据到达;二是程序立即返回并标识一个读错误。前者我们称之为阻塞式Socket,后者为非阻塞式Socket,在非阻塞式的情况下,程序开发者应当做出适当的处理。一般有两种处理方法:第一种为轮询法,即程序周期的去读Socket;第二种是较好的一种方法,异步通知法,即当Socket发生事件时,能够通知应用程序,如:Socket收到远方主机发来的数据时,Socket会产生一个"读事件",应用程序便可从Socket中读出数据了。
二、TCP/IP编程实例
1.客户方软件的编制:
下面的例子假设读者对VB编程有一定的了解,其工作流程是:客户方发出的一串信息到服务方,服务方收到后将收到的信息全部传回来。
首先用VB创建一个带有三个标记、三个文本控件、一个控制按纽及一个SocketWrench控件,当使用时,用户在Text1中输入远端主机IP的地址或主机名,将要发送的信息输入到Text2中,服务方返回的信息便在Text3中显示。Text2及Text3的Enable属性在初始时设为False。程序如下:
Sub Form Load()
Socket1.AddressFamily=AF INET
Socket1.Protoco1=IPPROTO IP
Socket1.Type=SOCK STREAM
Socket1.Binary=False
Socket1.BufferSize=1024
Socket1.Blocking=False
End Sub
当点击Connect后,便可建立同远方主机的连接,其过程如下:
Sub Command Click()
Socket1.HostName=Trim$(Text1.Text)
Socket1.RemotePort=IPPORT ECHO
Socket1.Action=SOCKET CONNECT
End Sub
在初始化的过程中,我们已将其Socket定为非阻塞式的(Socket1.Blocking=Fasle),因此它不必等待连接建立完,而是直接返回,并等待connect事件,当此事件发生后对其进行响应,程序如下:
Sub Socket1 Connect()
Text2.Enabled=True
Text3.Enabled=True
End Sub
此时,Text2,Text3便可进行数据的输入/输出了。下一步的程序为真正的收发过程,在Text2控件中加入KeyPress事件:
Sub Text2 KeyPress(KeyAscii As Integet)
If KeyAscii=13 Then
Socket1.SendLen=Len(Text2.Text)
Socket1.SendData=Text2.Text
KeyAscii=0:Text2.Text=""
End If
End Sub
当按下Enter时(KeyPress=13)数据便会发往远程主机发送数据,远程主机接到数据并传回,并在客户方产生一个读事件,处理读事件的过程如下:
Sub Socket1 Read(DataLength As Integer,IsUrgent As Integer)
Socket1.RecvLen=DataLength
Text3.Text=Socket1.RecvData
End Sub
当终结此连接时,在Form unload事件处加如下过程:
Sub Form Unload(Cancel As Integer)
If Socket1.Connected Then Socket1.Action=SOCKET CLOSE
End If
End Sub
到此,一个较为完整的VB程序已经完成,但是如果输入IP的地址或主机名不正确时,还需要编一个处理程序,这已超过本篇介绍的目的,在此就不做介绍了.
2、服务方软件的编制
服务方软件的第一件事情就是监听是否有连接的请求,当SocketWrench收到连接请求时,就会产生一个Accept事件,这时有两种处理方法:一是将Action属性设置为SOCKET ACCEPT;二是对Accept属性进行设置。前者较为简单但有局限性,因为它将结束监听其它的连接请求,只能同第一个客户建立连接。后者较为灵活但较为复杂,其方法是对Accept设置属性。然而,正在监听的控件不能设置Accept属性,必须利用闲置的Socket来对其进行设置,因此就需要一个控件组来处理多个连接。为方便起见,我们还利用前面使用的Form来编制服务方的应用。首先是再加上一个SocketWrench控件,并使之成为控件组。开始时此控件组中只有一个控件。称为Socket2(0),这个控件组用来监听连接请求,同客户方一样,也需先对其进行初始化:
Sub Form Load()
Socket1.AddressFamily=AF INET
Socket1.Proyocol=IPPROTO IP
Socket1.Type=SOCK STREAM
Socket1.Binary=False
Socket1.BufferSize=1024
Socket1.Blocking=False
Socket2(0) .Addr essFamily=AF INET
Socket2(0) .Proyocol=IPPROTO IP
Socket2(0) .Type=SOCK STREAM
Socket2(0) .Blocking=False
Socket2(0) .Localport=IPPORT ECHO
Socket2(0) .Action=SOCKET LISTEN
LastSocket=0
End Sub
其中local port指明了当接到对方数据时,将其全部传回的一个TCP系统应用。当服务器收到连接请求时,就会出现一个Accept事件,相应的处理过程如下:
Sub Socket2 Accept(Index As Integer,Sockerid As Integer)
Dim I As Integer
For I=1 To LastSocket
If Not Socket2(I).Connected Then Exit For
Next I
If I>LastSocket Then
LastSocket=LastSocket+1:I=LastSocket
Load Socket2(I)
End If
Socket2(I).AddressFamily=AF INET
Socket2(I).Protocol=IPPROTO IP
Socket2(I).Type=SOCK STREAM
Socket2(I).Binary=True
Socket2(I).BufferSize=1024
Socket2(I).Blocking=False
Socket2(I).Accept=SocketId
End Sub
以上程序收到一个连接时,产生一个新的SocketWrench控件,在对其进行初始化之后,便可同客户方通讯了.原Socket继续监听是否有新的连接请求。服务方收到数据后,将全部数据再传回客户方,其处理过程如下;
Sub Socket2 Read(Index As Integer,DataLength As Integer,IsUrgent As Integer)
Socket2(Index).RecvLen=DataLength
Socket2(Index).SendLen=DataLength
Socket2(Index).SendData=Socket2(Index).RecvData
End Sub
当客户方关闭连接,服务方也关闭相应Socket的控制,过程如下:
Sub Socket2 Close(Index As Integer)
Socket2(Index).Action=SOCKET CLOSE
End Sub
断开所有连接的程序如下:
Sub From Unload(Cancel As Integer)
Dim I As Integer
If Socket1.Connected Then Socket1.Action=SOCKET CLOSE
If Socket2(0).Listening Then Socket2(0).Action=SOCKET CLOSE
For I=1 To LastSocket
If Socket2(0).Connected Then Socket2(0).Action=SOCKET CLOSE
Next I
End
End Sub
到这里,我们就可以使用这个程序利用TCP/IP协议进行信息交换了。