PowerBuilder Sockets程序设计

本文转自网络,如侵犯蒋老师的版权,请留言删除。

PowerBuilder Sockets程序设计
蒋东兴
http://www.cic.tsinghua.edu.cn/jdx/book2/ws2chpc12z.htm


12.1  PowerBuilder网络程序概述
       在PowerBuilder 5.0发布之前,PowerBuilder应用程序要实现网络通信是很困难的。为了解决此问题,PowerBuilder 5.0提供了网络程序接口,它是以应用程序示例的形式给出的。要安装PowerBuilder网络程序接口,在安装PowerBuilder 5.0 Enterprise for Intel 32产品时,需选中Application Gallery部件,并选中该部件中的FTP/Winsock *部件,这样,PowerBuilder 5.0安装后在目录Powersoft/PowerBuilder 5.0/Application Gallery/Ftp中将会出现下述文件:

FTPTEST.PBL(FTP客户程序PowerBuilder源文件)

PBFTP.DOC(winword格式的使用说明)

PBFTP.RTF(Rich Text Format格式的使用说明)

PBWS.C(PBWS.DLL的C语言源文件)

PBWS.DLL(提供16位版本的getservbyname()和gethostbyname()函数的PB版本)

PBWS32.C(PBWS32.DLL的C语言源文件)

PBWS32.DLL(提供32位版本的getservbyname()和gethostbyname()函数的PB版本)

RFC640.TXT(FTP协议的RFC文档)

RFC959.TXT(FTP协议的RFC文档)

SOCKETS.PBL(Winsock用户对象PowerBuilder源文件,它提供网络接口)

WHELLO.ICO(图标)

WHELLO.BMP(图象)

      在这些文件中,两个动态连接库(PBWS.DLL、PBWS32.DLL)是运行时需要的,它将函数getservbyname()和gethostbyname()封装为PowerBuilder容易调用的形式,具体封装可以阅读其对应源文件。文件SOCKETS.PBL是网络程序接口的核心,它定义了PowerBuilder的网络部件,包括四个用户对象和三个结构。四个用户对象分别为:

·         u_winsock:Winsock函数基本类;

·         u_winsock_16:16位Winsock函数类;

·         u_winsock_32:32位Winsock函数类;

·         u_socket:Winsock对象。

三个结构为:

s_linger /* Winsock linger structure for setsockopt */

integer     l_onoff;

integer     l_linger;

 

s_sockaddr    /* Winsock sockaddr structure for connect and bind */

integer                   sin_family;

unsignedinteger      sin_port;

character               sin_addr[4];

character               sin_zero[8];

 

s_wsadata      /* Winsock WSAData for WSAStartup */

unsignedinteger      version;

unsignedinteger      highversion;

character               description[257];

character               systemstatus[129];

unsignedinteger      maxsockets;

unsignedinteger      maxupddg;

string                    vendorinfo;

       PowerBuilder应用程序可以通过使用这些用户对象与结构来实现与其它socket应用程序之间的相互网络通信。

12.2  使用Winsock函数
      为了使用Winsock的函数,PowerBuilder定义了三个对象:Winsock函数基本类u_winsock、16位Winsock函数对象u_winsock_16和32位Winsock函数对象u_winsock_32。

 用户对象u_Winsock是Winsock的基本类,定义它主要是为了声明的局部外部函数,这些函数包括Winsock DLL提供的函数、Windows函数、以及一些为PowerBuilder编写的Winsock函数(如GetHost)。下面是这些函数的声明。

FUNCTION int WSAStartup( uint wVersionRequested, ref s_wsadata lpWSAData ) LIBRARY "winsock.dll"

FUNCTION int WSACleanup() LIBRARY "winsock.dll"

FUNCTION int ntohs(int netshort) LIBRARY "winsock.dll"

FUNCTION int htons(int hostshort) LIBRARY "winsock.dll"

FUNCTION int WSAGetLastError (  ) LIBRARY "winsock.dll"

FUNCTION int getsockname(uint s,ref s_sockaddr name, ref int  namelen) LIBRARY "winsock.dll"

FUNCTION int setsockopt (uint socket, int level, int optname,s_linger optval, int optlen ) LIBRARY "winsock.dll"

FUNCTION uint socket(int af, int ttype, int protocol) LIBRARY "winsock.dll"

FUNCTION int WSAAsyncSelect (uint socket, uint Wnd, uint wMsg, long lEvent ) LIBRARY "winsock.dll"

FUNCTION int bind (uint s, s_sockaddr name, int namelen ) LIBRARY "winsock.dll"

FUNCTION int listen(uint s, int backlog)  LIBRARY "winsock.dll"

FUNCTION uint accept (uint s,s_sockaddr addr, ref int addrlen ) LIBRARY "winsock.dll"

FUNCTION int closesocket ( uint socket ) LIBRARY "winsock.dll"

FUNCTION int wsconnect (uint socket, s_sockaddr name, int namelen ) LIBRARY "winsock.dll" ALIAS for "connect"

FUNCTION int WSACancelBlockingCall ( ) LIBRARY "winsock.dll"

FUNCTION int recv ( int socket, ref blob buf, int len, int flags ) LIBRARY "winsock.dll"

FUNCTION int send ( int socket, ref blob buf, int len, int flags ) LIBRARY "winsock.dll"

 

FUNCTION string GetHost(string lpszhost, ref string lpszaddress ) library "pbws.dll"

FUNCTION int GetService(string lpszSevicet,string lpszProto ) library "pbws.dll"

 

FUNCTION boolean KillTimer(uint Wnd, int IDEvent) LIBRARY "user.exe"

FUNCTION uint SetTimer(uint Wnd, int IDEvent, uint Elapse, ulong Dummy) LIBRARY "user.exe"

       对用户而言,如果需要用到其它的Winsock函数,他还可以在这里添加需要声明的其它函数。如:

FUNCTION long ntohl(long netlong) LIBRARY "winsock.dll"

FUNCTION long htonl(long hostlong) LIBRARY "winsock.dll"

另外,如果用户需要引用其它DLL中的函数,也可以使用同样的方法实现。如增加一对ntohl()和htonl()函数:

FUNCTION long ntohl(long netlong) LIBRARY "winsock.dll"

FUNCTION long htonl(long hostlong) LIBRARY "winsock.dll"

       由于Windows 95系统中使用的Winsock.dll中的函数是16位版本,因此用户对象u_winsock_16引用的局部外部函数和u_winsock是一样的,这里就不再列举。

       为了使32位的应用程序能够更好地使用32位版本的Winsock函数,用户对象u_winsock_32引用的局部外部函数都是32位版本的,这些函数的声明如下:

FUNCTION int WSAStartup( uint wVersionRequested, ref s_wsadata lpWSAData ) LIBRARY "wsock32.dll"

FUNCTION int WSACleanup() LIBRARY "wsock32.dll"

FUNCTION int ntohs(int netshort) LIBRARY "wsock32.dll"

FUNCTION int htons(int hostshort) LIBRARY "wsock32.dll"

FUNCTION int WSAGetLastError (  ) LIBRARY "wsock32.dll"

FUNCTION int getsockname(uint s,ref s_sockaddr name, ref int  namelen) LIBRARY "wsock32.dll"

FUNCTION int setsockopt (uint socket, int level, int optname,s_linger optval, int optlen ) LIBRARY "wsock32.dll"

PRIVATE FUNCTION uint socket(int af, int ttype, int protocol) LIBRARY "wsock32.dll"

PRIVATE FUNCTION int WSAAsyncSelect (uint socket, uint Wnd, uint wMsg, long lEvent ) LIBRARY "wsock32.dll"

PRIVATE FUNCTION int bind (uint s, s_sockaddr name, int namelen ) LIBRARY "wsock32.dll"

PRIVATE FUNCTION int listen(uint s, int backlog)  LIBRARY "wsock32.dll"

PRIVATE FUNCTION uint accept (uint s,s_sockaddr addr, ref int addrlen ) LIBRARY "wsock32.dll"

PRIVATE FUNCTION int closesocket ( uint socket ) LIBRARY "wsock32.dll"

PRIVATE FUNCTION int wsconnect (uint socket, s_sockaddr name, int namelen ) LIBRARY "wsock32.dll"ALIAS for "connect"

PRIVATE FUNCTION int WSACancelBlockingCall ( ) LIBRARY "wsock32.dll"

PRIVATE FUNCTION int recv ( int socket, ref blob buf, int len, int flags ) LIBRARY "wsock32.dll"

PRIVATE FUNCTION int send ( int socket, ref blob buf, int len, int flags ) LIBRARY "wsock32.dll"

 

PRIVATE FUNCTION string GetHost(string lpszhost, ref string lpszaddress ) library "pbws32.dll"

PRIVATE FUNCTION int GetService(string lpszSevicet,string lpszProto ) library "pbws32.dll"

 

PRIVATE FUNCTION boolean KillTimer(uint Wnd, int IDEvent) LIBRARY "user32.dll"

PRIVATE FUNCTION uint SetTimer(uint Wnd, int IDEvent, uint Elapse, ulong Dummy) LIBRARY "user32.dll"

12.3  Winsock对象u_socket
用户对象u_socket是PowerBuilder 5.0实现Sockets网络通信的关键,它通过定义一系列实例变量(Instance Variables)和用户对象函数(User Object Function)将Winsock函数封装起来,使得PowerBuilder程序员能够方便地使用Winsock函数。

12.3.1  u_socket定义的实例变量
       用户对象u_socket定义了一系列实例变量,这些变量包括Winsock函数类、Winsock函数要使用的数据结构、套接字状态和一些宏定义等。下面我们在给出这些实例变量列表的同时,给出每一个变量的简要说明。

       PUBLIC u_winsock ws  // Winsock函数类的实例变量,u_socket通过使用用户对象u_winsock(u_winsock_16或u_winsock_32)的一个实例来实现对Winsock函数的调用。

PRIVATE uint iui_handle       // 用户对象u_socket的实例句柄。

PRIVATE uint iui_messagenum = 1024              // 常数,其值等于pbm_custom01,定义套接字处理的消息号,即WSAAsyncSelect()函数的wMsg参数值。

PRIVATE boolean ib_TimesUp     // 用于函数wait(),为定时到期标志

PUBLIC PRIVATEWRITE s_wsadata ius_wsadata // 函数WSAStartup()使用的结构变量

PUBLIC PRIVATEWRITE us_socket ius_socket  // 描述对象内部状态的结构变量

       地址信息变量:

PUBLIC string is_hostname   // 主机名

PUBLIC string is_hostaddress  // 主机IP地址,按网络字节顺序存放

PUBLIC int ii_portno     // 端口号

       套接字状态:

PUBLIC PRIVATEWRITE int status_OK=0               // 正常状态

PUBLIC PRIVATEWRITE int status_Failure = -1       // 失败状态

PUBLIC PRIVATEWRITE int status_timeout = 1              // 超时状态

      超时值(单位:毫秒):

PUBLIC uint iui_ConnectTimeout = 30000 // 连接超时值

PUBLIC uint iui_SendTimeout = 30000       // 发送超时值

PUBLIC uint iui_ReceiveTimeout = 30000    // 接收超时值

      套接字工作状态:

PUBLIC PRIVATEWRITE int state_Idle = 0                     // 空闲:套接字建立后初始状态

PUBLIC PRIVATEWRITE int state_Open = 1            // 打开:未用

PUBLIC PRIVATEWRITE int state_Closed = 2          // 关闭:套接字使用closesocket()函数关闭后或recv()函数接收到0字节数据

PUBLIC PRIVATEWRITE int state_Connected = 3     // 已连接:套接字使用accept()函数接收连接成功或使用函数connect()连接远程主机成功

PUBLIC PRIVATEWRITE int state_Connecting = 4    // 正连接:套接字使用connect()函数试图和远程主机建立连接时

PUBLIC PRIVATEWRITE int state_Listening = 5       // 监听:被动套接字使用listen()函数成功返回时状态,使用accept()时状态改变,在accept()函数成功返回后被动套接字恢复为此状态

PUBLIC PRIVATEWRITE int state_Accepting = 6     // 正接收:套接字使用accept()函数试图接收远程主机连接时

PUBLIC PRIVATEWRITE int state_Accepted = 7      // 已接收:套接字使用accept()函数成功返回

PUBLIC PRIVATEWRITE int state_Error = 8            // 错误:任何出错状态

结构长度:

PUBLIC PRIVATEWRITE int sizeof_IN_ADDR = 4   // 结构in_addr的长度

PUBLIC PRIVATEWRITE int sizeof_LINGER = 4     // 结构linger的长度

PUBLIC PRIVATEWRITE int sizeof_SOCKADDR = 16     // 结构sockaddr_in的长度

      用到的套接字常数:

PRIVATE int PF_INET = 2                // Internet协议族

PRIVATE int SOCK_STREAM = 1     // 流套接字类型(TCP)

PRIVATE int IPPROTO_IP = 0 // 虚构的IP协议号,用作socket()函数的protocol参数

PRIVATE long FD_READ = 1      // 函数WSAAsyncSelect()使用的消息值:可读

PRIVATE long FD_WRITE = 2    // 函数WSAAsyncSelect()使用的消息值:可写

PRIVATE long FD_OOB = 4        // 函数WSAAsyncSelect()使用的消息值:带外数据

PRIVATE long FD_ACCEPT= 8   // 函数WSAAsyncSelect()使用的消息值:有外来连接

PRIVATE long FD_CONNECT = 16// 函数WSAAsyncSelect()使用的消息值:连接建立

PRIVATE long FD_CLOSE = 32  // 函数WSAAsyncSelect()使用的消息值:连接关闭

PUBLIC PRIVATEWRITE int SOL_SOCKET = -1     // 套接字层选项的层,用于函数getsockopt()和setsockopt()的level参数

PUBLIC PRIVATEWRITE int SO_LINGER = 128      // 套接字选项:套接字上有数据时关闭操作延迟

PRIVATE uint INVALID_SOCKET = 65535       // 非法套接字,多用于函数返回值

PRIVATE int SOCKET_ERROR = -1   // 套接字出错,多用于函数返回值

PRIVATE int WSAEWOULDBLOCK = 10035     // 错误码:指示非阻塞套接字不能立即成功返回,发布的操作将被阻塞

12.3.2  u_socket定义的结构
u_socket定义了两个用户对象结构:主机事件结构us_hostent和描述套接字内部状态的结构us_socket。其结构定义如下:

us_hostent  // 主机事件结构hostent,

string                    h_name          // 主机正式名

string                    h_aliases         // 主机别名

integer                  h_addrtype     // 主机地址类型

integer                  h_length         // 地址长度

string                    h_addr_list      // 地址列表

      

us_socket     // 描述套接字内部状态的结构

integer                  state        // 套接字工作状态(监听、连接、关闭,…等)

integer                  status      // 套接字状态(正常、出错、超时等)

boolean                 datain      // 套接字上有数据可读

boolean                 sendok    // 套接字可以发送数据

integer                  errmsg    // 套接字的错误码(WSAGetLastError()的返回值)

unsignedinteger      socketnumber // 套接字描述符

12.3.3  u_socket的事件处理程序
       u_socket使用了四个事件:constructor、destructor、ue_socket_notification和ue_timer,其中ue_socket_notification和ue_timer是用户自定义事件。

       事件constructor在用户对象创建时发生,它主要用来启动Winsock并设置套接字的初始状态值。其代码如下:

int rc

environment e

this.visible = FALSE

iui_handle = Handle(this)      // 用户对象u_socket的实例句柄

// 根据操作系统信息,实例化Winsock函数对象

GetEnvironment(e)

if e.Win16 then

         ws = create u_winsock_16

else

         ws = create u_winsock_32

end if

// 初始化socket内部状态

ius_socket.DataIn = FALSE

ius_socket.SendOK = FALSE

ius_socket.State = state_Idle

ius_socket.ErrMsg = 0

is_hostaddress = "~000~000~000~000"

ii_portno = 0

// 启动Winsock

rc = ws.WSAStartup(257,ius_wsadata)            // 257=0x0101,指示Winsock版本1.1

if rc = 0 then

         ius_socket.Status = status_OK

else

         ius_socket.Status = status_Failure

         ius_socket.ErrMsg = rc

end if

 

      事件destructor在用户对象消亡时发生,它主要用来关闭Winsock并释放资源。其代码如下:

ws.WSACleanup()

destroy ws

 

      事件ue_socket_notification处理用户自定义的Winsock事件pbm_custom01,该事件是用户调用u_socket的用户对象函数uf_open打开套接字时注册的。其代码如下:

long l

l = Message.LongParm

choose case WSAGetSelectEvent(l)

         case FD_CONNECT   // 连接建立完成

                   SocketConnected( l )

         case FD_READ           // 有数据可读

                   SocketRead( l )

      case FD_WRITE                   // 套接字可写

                   SocketWrite( l )

   case FD_CLOSE             // 套接字上的连接关闭

                   SocketClose( l )

   case FD_ACCEPT          // 有外来连接到来

                   SocketAccept( l )

   case else                                    // 其它事件

               SocketOther( l )

end choose

      事件ue_timer处理用户自定义事件pbm_timer,它释放定时器,并通过设置变量ib_TimesUp为True来通知等待函数。此事件由wait函数设置的定时器产生,其代码如下:

ws.KillTimer(iui_handle,1)

ib_TimesUp = True

12.3.4  u_socket定义的用户对象函数
       u_socket定义了21个用户对象函数,这些函数可以分为三类:Winsock事件处理函数、套接字操作函数和其它函数。这些函数的定义及其功能可以用表12.1简单描述:

表12.1  u_socket定义的用户对象函数表

类型
 语           法
 说      明
 


 socketaccept(long pl_info)
 FD_ACCEPT事件处理函数
 
socketclose(long pl_info)
 FD_CLOSE事件处理函数
 
socketconnected(long pl_info)
 FD_CONNECT事件处理函数
 
socketother(long pl_info)
 其它事件处理函数
 
socketread(long pl_info)
 FD_READ事件处理函数
 
socketwrite(long pl_info)
 FD_WRITE事件处理函数
 
 

 

 


 uf_acceptconnection(integer pi_accepttimeout, window pw_socketholder) return u_socket
 接收连接
 
uf_close() return boolean
 关闭连接
 
uf_connecttohost() return boolean
 建立连接
 
uf_dobind() return boolean
 捆绑套接字
 
uf_dolisten(integer pi_backlog) return boolean
 让套接字监听连接(用于服务器)
 
uf_gethost(string ps_host) return boolean
 获取主机名字与地址
 
uf_getservice(string ps_service, string ps_proto) return boolean
 获取Internet服务的端口号
 
uf_inet_ntoa() return string
 将网际地址转换为字符串形式
 
uf_open() return boolean
 创建套接字
 
uf_receivedata(blob pb_buffer) return integer
 从已连接的套接字上读数据
 
uf_senddata(blob pb_buffer, pi_buffersize) return integer
 向已连接的套接字上写数据
 
 


 wait(unsignedinteger pui_miliseconds)
 等待函数(放弃CPU)
 
wsagetselecterror(long pl_error) return long
 从事件参数中提取错误码
 
wsagetselectevent(long pl_event) return long
 从事件参数中提取事件代码
 
debug(string ps_text)
 调试函数
 

       在上述函数中,事件处理函数是u_socket的私有用户对象函数,它们用来处理Winsock的相应网络事件;套接字操作函数是公用用户对象函数,它们用来实现对套接字的创建、连接、读/写、关闭等操作,应用程序通过调用u_socket的这些公用用户对象函数来实现Winsock的网络通信;其它函数是几个私有用户对象函数,其中的两个事件参数提取函数相当于Winsock定义的同名的宏。下面我们在每类函数中选取一个例子来看看它们所作的工作。

 

      事件处理函数socketread(long pl_info)

       此函数用来处理Winsock的数据可读事件FD_READ,它只是设置套接字的内部状态为可读(DataIn = True),而没有作实际的读数据操作。其代码如下:

ius_socket.DataIn = True

 

if WSAGetSelectError(pl_info) <> 0 then

         ius_socket.State = state_error

    ius_socket.Status = status_failure

         ius_socket.ErrMsg = WSAGetSelectError(pl_info)

else

         ius_socket.Status = status_ok

         ius_socket.ErrMsg = 0

end if

 

      套接字操作函数uf_receivedata(blob pb_buffer) return integer

       此函数用来从已连接套接字上读取数据。它首先设置套接字内部状态为无数据可读(DataIn = False),然后从套接字上读取数据。如果recv操作成功返回,则说明读数据完成,设置套接字状态正常(status = status_OK)并返回读到的字节数。如果recv操作出错,则获取错误码,并判断是否因套接字上数据可读而操作可能阻塞产生的错误(WSAEWOULDBLOCK),如果是则等待,否则设置套接字状态为出错(status = status_failure)并设置错误码ErrMsg后返回。在等待时,如果套接字上有数据可读(由FD_READ事件的处理程序设置了套接字内部状态DataIn = True)或等待超时,则结束等待。如果是有数据可读结束的等待,则重新调用recv()函数读取数据;如果是超时结束的等待,则设置套接字状态为出错后返回。其代码如下:

 

int err, bytesread

uint tout

 

ius_socket.DataIn = False     // 设置套接字内部状态为无数据可读

bytesread = ws.recv(ius_socket.SocketNumber, pb_buffer, Len(pb_buffer), 0)    // 读数据

if bytesread = SOCKET_ERROR then   // 如果出错返回

         err = ws.WSAGetLastError()                  // 获取错误码

         if err = WSAEWOULDBLOCK then      // 错误原因为可能阻塞

                   for tout = 100 to iui_receivetimeout step 100           // 等待

                            wait(100)

                            if ius_socket.DataIn then exit         // 等待中有数据可读了则退出等待状态

                   next

                   if ius_socket.DataIn and ius_socket.ErrMsg = 0 then         // 有数据可读

                            ius_socket.DataIn = False     // 置套接字内部状态为数据已读完

                            do     // 读取数据,如果阻塞则循环读

                                     bytesread=ws.recv(ius_socket.SocketNumber,pb_buffer,Len(pb_buffer),0)

                                     if bytesread <> SOCKET_ERROR then exit

                            LOOP UNTIL ws.WSAGetLastError() <> WSAEWOULDBLOCK

                            if bytesread = SOCKET_ERROR then   // 读数据出错,则设置状态为出错

                                     ius_socket.ErrMsg = ws.WSAGetLastError()

                                     ius_socket.Status = status_failure

                            else   // 读数据成功,设置状态为正常

                                     ius_socket.status = status_ok

                            end if

                   else   // 超时退出循环,设置套接字状态为出错

                            ius_socket.Status = status_failure

                            if not ius_socket.DataIn then // should probably cancel blocking call

                                     ius_socket.ErrMsg = -1

                            else

                                     ius_socket.DataIn = False

                            end if

                   end if

         else   // 其它错误导致的recv操作出错,设置套接字状态为出错

                   ius_socket.ErrMsg = err

                   ius_socket.status = status_failure

         end if

else   // 读取数据成功,设置套接字状态为正常

         ius_socket.status = status_OK

         ius_socket.ErrMsg = 0

end if

 

// 如果读取的字节数为0,则说明套接字被对等方关闭

if bytesread = 0 then ius_socket.state = state_Closed       

return bytesread  // 返回字节数

                           

      其它函数wsagetselectevent(long pl_event) return long

       此函数相当于Winsock定义的宏WSAGETSELECTEVENT,它从事件通知的参数中提取网络事件。其代码为:

return Mod(pl_event,65536) // loword

12.4  应用程序示例
       我们用一个简单的点对点网络实时通信程序来示例PowerBuilder网络程序的设计。该实例分两部分:客户程序与服务器程序。其工作过程是:服务器首先启动,它创建套接字之后等待客户的连接;客户启动后,创建套接字,然后和服务器建立连接;连接建立后,客户接收键盘输入,每接收到一行则将数据发送到服务器,服务器收到数据后,计算字符长度并将字符串发送回来,客户将收到的数据在窗口中显示;如此循环,直到客户程序使用关闭命令时,它关闭连接和套接字后退出。服务器在用户关闭窗口时关闭套接字然后退出。

12.4.1  客户程序
       创建一个新的应用程序pbechoc,将sockets.pbl中的用户对象u_socket,u_winsock,u_winsock16,u_winsock32和数据结构s_linger,s_sockaddr,s_wsadata拷贝到新的PowerBuilder程序库pbechoc.pbl中。建立客户程序主窗口w_main如图12.1所示,并将应用程序pbechoc的启动代码设置为打开此窗口。


图12.1  客户程序主窗口

       按钮“Open Connection”的作用是打开和服务器的连接,它主要完成下述工作:创建用户对象u_socket的实例;根据用户输入的服务器主机名获取远程主机地址;根据用户输入的服务名获取服务的端口号和协议等信息;创建套接字,并和服务器建立连接。其Clicked事件的脚本(script)为:

string s

 

parent.OpenUserObject(iu_socket, "u_socket")        /* 创建用户对象u_socket的实例 */

if iu_socket.ius_socket.Status = iu_socket.status_OK then

         mle_1.text = iu_socket.ius_wsadata.description

else

         mle_1.text = "Error"

end if

 

if not iu_socket.uf_GetHost(sle_1.text) then   /* 获取远程主机地址 */

         mle_1.text = mle_1.text + "~r~n" + "GetHostByName Failed"

else

         s = String(Asc(Mid(iu_socket.is_hostaddress,1,1)))

         s = s + "." + String(Asc(Mid(iu_socket.is_hostaddress,2,1)))

         s = s + "." + String(Asc(Mid(iu_socket.is_hostaddress,3,1)))

         s = s + "." + String(Asc(Mid(iu_socket.is_hostaddress,4,1)))

   mle_1.text = mle_1.text + "~r~n" + iu_socket.is_hostname + "~r~n" + "Host: " + s

end if

 

if not iu_socket.uf_GetService(sle_2.text,'tcp') then /* 获取服务的端口号和协议等信息 */

         mle_1.text = mle_1.text + "~r~n" + "GetServByName Failed"

else

   mle_1.text = mle_1.text + "~r~nPort: " + String(iu_socket.ws.ntohs(iu_socket.ii_portno))

end if

 

if iu_socket.uf_open() then   /* 创建套接字 */

         mle_1.text = mle_1.text + "~r~n" + "Open succeeds"

         if iu_socket.ius_socket.status = iu_socket.status_OK then

                   mle_1.text = mle_1.text + "-state OK status=" + string(iu_socket.ius_socket.status)

         else

                   mle_1.text = mle_1.text + " - state failed"  + string(iu_socket.ius_socket.status)

         end if

else

         mle_1.text = mle_1.text + "~r~n" + "Open failed" + string(iu_socket.ius_socket.status)

end if         

 

if iu_socket.uf_connecttohost() then /* 和服务器建立连接 */

         mle_1.text = mle_1.text + "~r~n" + "Connected."

else

         mle_1.text = mle_1.text + "~r~n" + "Not Connected – ErrMsg = " + &

   String(iu_socket.ius_socket.ErrMsg)

end if

 

       按钮“Close Connection”的作用是关闭和服务器的连接。为了通知服务器作相应的关闭连接处理,客户程序先给服务器程序发送一个协商好的字符串“Quit”,然后在关闭套接字及用户对象u_socket的实例。其Clicked事件的脚本为:

blob{4096} buffer

int bytesread

 

/* 给服务器发送“Quit”消息 */

buffer = Blob("Quit")

bytesread = iu_socket.uf_SendData(buffer, Len(String(buffer)))

if iu_socket.ius_socket.Status <> iu_socket.status_ok then

         mle_1.text = mle_1.text + "~r~nError: State = " + string(iu_socket.ius_socket.state) + &

                    " ErrMsg = " + string(iu_socket.ius_socket.ErrMsg)

end if

 

if iu_socket.uf_close() then   /* 关闭套接字 */

         mle_1.text = mle_1.text + "~r~n" + "close succeeds"

         if iu_socket.ius_socket.status = iu_socket.status_OK then

                   mle_1.text = mle_1.text + " - state OK status " + string(iu_socket.ius_socket.status)

         else

                   mle_1.text = mle_1.text + "-state failed status"+ string(iu_socket.ius_socket.status)

         end if

else

         mle_1.text = mle_1.text + "close failed status " + string(iu_socket.ius_socket.status)

end if         

 

parent.CloseUserObject(iu_socket) /* 关闭用户对象u_socket的实例 */

 

       单行文本输入框“Input”用于用户输入,它接收用户输入并将其发送给服务器,然后接收服务器的回应(echo),并显示在多行文本框中。其Modified事件的脚本为:

blob{4096} buffer

int bytesread

 

buffer = Blob(sle_3.text)

bytesread = iu_socket.uf_SendData(buffer, Len(String(buffer))) /* 发送用户输入字符串 */

if iu_socket.ius_socket.Status <> iu_socket.status_ok then

         mle_1.text = mle_1.text + "~r~nError: State = " + string(iu_socket.ius_socket.state) + &

                    " ErrMsg = " + string(iu_socket.ius_socket.ErrMsg)

end if

 

bytesread = iu_socket.uf_ReceiveData(buffer) /* 接收服务器回应 */

if iu_socket.ius_socket.Status = iu_socket.status_ok then

         if bytesread > 0 then

                   mle_1.text = mle_1.text + "~r~n" + String(buffer)

                   mle_1.Scroll(mle_1.LineCount())

         end if

else

         mle_1.text = mle_1.text + "~r~nError: State = " + string(iu_socket.ius_socket.state) + &

                    " ErrMsg = " + string(iu_socket.ius_socket.ErrMsg)

end if

 

sle_3.SelectText(1, Len(sle_3.Text))

12.4.2  服务器程序
       创建一个新的应用程序pbechos,将sockets.pbl中的用户对象u_socket,u_winsock,u_winsock16,u_winsock32和数据结构s_linger,s_sockaddr,s_wsadata拷贝到新的PowerBuilder库pbechos.pbl中。建立客户程序主窗口w_main如图12.2所示,并将应用程序pbechos的启动代码设置为打开此窗口。


图12.2  服务器程序主窗口

       服务器的程序比较简单,它只需编写窗口的“Open”事件的脚本程序。这段程序完成一个套接字服务器的创建,并接收客户程序的连接;在新建连接的套接字上接收客户方发送的数据,并将其发送回客户程序;如果接收到“Quit”命令,则关闭套接字,回到服务器套接字等待接收连接状态。其脚本为:

u_socket iu_newsock

blob{4096} buffer

int bytesread

 

This.Show()

This.OpenUserObject(iu_socket,"u_socket") /* 创建用户对象u_socket的实例 */

if iu_socket.ius_socket.Status = iu_socket.status_OK then

         mle_1.text = mle_1.text + "~r~n" + iu_socket.ius_wsadata.description

else

         mle_1.text = "Error"

end if

 

if not iu_socket.uf_GetService('SjC160','tcp') then /* 获取服务信息 */

         mle_1.text = mle_1.text + "~r~n" + "GetServByName Failed"

else

   mle_1.text = mle_1.text + "~r~nPort: " + String(iu_socket.ws.ntohs(iu_socket.ii_portno))

end if

 

if iu_socket.uf_open() then /* 创建套接字 */

         mle_1.text = mle_1.text + "~r~n" + "Open succeeds"

         if iu_socket.ius_socket.status = iu_socket.status_OK then

                   mle_1.text = mle_1.text + "-state OK status=" + string(iu_socket.ius_socket.status)

         else

                   mle_1.text = mle_1.text + " - state failed"  + string(iu_socket.ius_socket.status)

         end if

else

         mle_1.text = mle_1.text + "~r~n"

end if         

 

if iu_socket.uf_dobind() then /* 绑扎本地地址 */

   mle_1.text = mle_1.text + "~r~n " + "Bind succeeds"

else

         mle_1.text = mle_1.text + "~r~n" + "Bind to local address Failed"

end if

 

if iu_socket.uf_dolisten(1) then /* 使套接字成为被动套接字,处于监听状态 */

   mle_1.text = mle_1.text + "~r~n " + "Listen succeeds"

else

         mle_1.text = mle_1.text + "~r~n" + "Listen Failed"

end if

 

accept1: /* 等待接收外来连接状态 */

iu_newsock = iu_socket.uf_AcceptConnection(30000, This) /* 接收客户程序的连接 */

if isNull(iu_newsock) then

         mle_1.text = mle_1.text + "~r~n" + "Accept Failed"

         goto accept1

else

   mle_1.text = mle_1.text + "~r~n " + "Accept succeeds"

end if

 

receive1: /* 等待接收数据状态 */

bytesread = iu_newsock.uf_ReceiveData(buffer) /* 接收客户程序发送的数据 */

if iu_newsock.ius_socket.Status = iu_socket.status_ok then

         if bytesread > 0 then

                   mle_1.text = mle_1.text + "~r~n" + String(bytesread) + " bytes =" + &

   Mid(String(buffer), 1, bytesread)

                   mle_1.Scroll(mle_1.LineCount())

                   if Mid(String(buffer), 1, 4) = "Quit" then /* 如果是“Quit”命令,则转关闭 */

                            goto close1

                   end if

         else

                   mle_1.text = mle_1.text + "~r~nOK but 0 bytes recvd"

         end if

else

         mle_1.text = mle_1.text + "~r~nError: State = " + string(iu_socket.ius_socket.state) + &

                    " ErrMsg = " + string(iu_socket.ius_socket.ErrMsg)

end if

 

bytesread = iu_newsock.uf_SendData(buffer, bytesread) /* 将接收到的数据发送回客户 */

if iu_newsock.ius_socket.Status <> iu_newsock.status_ok then

         mle_1.text = mle_1.text + "~r~nError: State = " + string(iu_newsock.ius_socket.state) &

                    + " ErrMsg = " + string(iu_newsock.ius_socket.ErrMsg)

end if

 

goto receive1 /* 循环接收数据 */

 

close1:

if iu_newsock.uf_close() then /* 关闭套接字 */

         mle_1.text = mle_1.text + "~r~n" + "close succeeds"

         if iu_newsock.ius_socket.status = iu_newsock.status_OK then

                   mle_1.text = mle_1.text +"-state OK status" + string(iu_newsock.ius_socket.status)

         else

                   mle_1.text =mle_1.text+"-state failed status"+string(iu_newsock.ius_socket.status)

         end if

else

         mle_1.text = mle_1.text + "close failed status "  + string(iu_newsock.ius_socket.status)

end if         

 

goto accept1 /* 循环接收客户程序的连接 */

 
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值