pb中的套接字是通过winsock.pbl库来提供的,他封装了套接字编程中用到的数据结构和过程,在功能上类似于vb中的winsock控件。
winsock.pbl中定义了两种类型的socket:流式socket和数据报式socket。流式socket需要连接到另一个处于监听状态的流式socket后才能进行通信,是基于连接的,其可靠性高;数据报式socket无需建立连接,源主机发出的报文在网络中经过存储转发后到达目的主机,效率高但可靠性低。编程时,根据应用环境和需求选择其中一种,若通信子网相当可靠,可考虑采用数据报式socket。
用pb编写winsock tcp/ip应用程式的第一步是将winsock.pbl加到应用程式中,然后声明如下全局变量:
winsock ws
boolean b?tcp?active
//用于检验ws是否初始化成功
powerobject gpo?null//全局空对象
在应用程式的open事件加入下列代码:
ws=create winsock
//初始化winsock的一个实例
setnull(gpo?null)//ws的函数中用到空对象gpo?null
在应用程式的close事件加入下列代码:
destroy ws//销毁ws对象
完成以上工作后,就能着手编程了,下面介绍怎么利用socket进行通信。
1.用数据报式socket向本机的7号端口发送数据
tcp和udp协议规定了传输层端口的长度为16比特,因此tcp和udp软件能使用216个不同的端口进行通信。尽管如此,编程时最佳不要使用前1024个端口,因为这个范围内非常多是专用端口,如21为ftp端口。本例中用到的7号端口非常特别,他回显接收到的所有数据,常用于端口检测。下面就向本机的7号端口发送数据报:
dgsock=create socketdgram
//创建数据报式socket对象
uladdr=ws.inet?addr(″127.0.0.1″)
//将本机ip地址转换为32位的ulong类型
buf=blob(″these data is send through datagram~r ~n″)//要发送的数据
dgsock.sendto(buf,len(buf),0,uladdr,7)
//向uladdr主机的7号端发送数据报
buf=blob(space(len(buf)))
//清空buf缓冲区
dgsock.recv(buf,len(buf),0)
//接收数据报
messagebox(′data received′,string(buf))
//显示接收到的数据
dgsock.closesocket()//关闭socket
destroy dgsock
从上面的演示能看出,发送到本机7号端口的数据报即时被反弹回来。
2.用流式socket 研发网络聊天程式
网络聊天程式通常包含两个部分:服务程式和客户程式。服务程式一直处于监听状态,当听到客户程式的呼叫时,就创建一个socket对他进行响应。下面用流式socket研发一个两节点聊天程式:
(1)编写服务程式
服务程式界面如图1所示。在主窗口的open事件中创建流式socket的一个实例:
ssock=create sockstream//ssock为实例变量
在“监听”按钮的clicked事件中加入下列代码:
uladdr=ws.inet?addr(″202.140.1.20″)
//将服务器地址转为ulong类型
ssock.bind(uladdr,2000)//将流式socket绑定到uladdr地址的2000号端口上
ssock.listen(5)//监听上述地址和端口,参数为请求队列长度,最大值为5
uisocktype=ssock.accept(ulclientaddr,iclientport)
//接受客户请求,参数填入了客户socket的地址和端口,返回值为客户socket类型
saccept=create socket
//创建一个socket响应客户请求
ulparam=1
saccept.initsocket(uisocktype)
//和客户socket类型相同
saccept.ioctlsocket(ws.fionbio,ulparam)
//异步模式
timer(0.5)
//启动定时器,以0.5秒的间隔接收数据
在timer事件中加入下列代码来处理到达的数据:
buf=blob(space(256))//定义缓冲区大小
saccept.recv(buf,len(buf),0)
//接收到达的数据
mle?1.text=mle?1.text+trim(string(buf))
//显示消息
在“发送”按钮的clicked事件中加入下列代码:
buf=blob(mle?2.text+″~r~n″)
//将mle?2中的内容放入发送缓冲区
saccept.send(buf,len(buf),0)
//将buf中的内容发给对方
mle?2.text=″ ″
//清除已发送的内容
在“退出”按钮的clicked事件中加入下列代码:
saccept closesocket()//关闭socket
destroy saccept
ssock.closesocket()
destroy ssock//清除socket
(2)编写客户程式
设计如图2所示的窗口,其open事件的代码为:
sclient=create socketstream
//创建流式socket
ulparam=1
//1表示异步模式(即非阻塞模式)
timer(0.5)//启动定时器,以0.5秒的间隔检查是否有数据到达
sclient.ioctlsocket(ws.fionbio, ulparam)
//将sclient设置为异步模式
在“连接” 按钮的clicked事件中加入下列代码:
uladdr=ws.inet?addr(″202.140.1.20″)
//服务器地址
if sclient.wsconnect(uladdr,2000)=-1 then//连接到服务器的2000号端口
messagebox(′socket′,″连接服务器失败″)
end if
timer事件和“发送”按钮的clicked事件的代码和服务程式相同,只需将套接字对象saccept改为sclient即可。
声明:缺省情况下创建的流式socket对象使用同步模式,可根据需要将其转换成异步模式。在同步模式下,一些winsock函数调用在完成处理之前不会把控制权还给程式,导致程式无响应。例如,在数据到达之前,recv()调用将一直处于等待状态。在上面的服务程式中,用于监听客户连接的socket使用了同步模式,响应客户请求的socket使用了异步模式,客户程式中的socket也使用了异步模式。
运行服务程式,点击“监听”进入等待状态;运行客户程式,点击“连接”进行呼叫。建立连接后,就能聊天了。在mle?2中输入消息,点击“发送”就可传给对方,对方发过来的消息显示在mle?1中