| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
共有 161 人关注过本帖
标题:增强版的foxsocket来了
只看楼主 加入收藏
sam_jiang
Rank: 10Rank: 10Rank: 10
等 级:贵宾
威 望:14
帖 子:1017
专家分:1535
注 册:2021-10-13
结帖率:97.7%
收藏
 问题点数:20 回复次数:4 
增强版的foxsocket来了
上次网友@easyppt 问:“能和网页客户端 通讯吗,这样,买家 网页发起客服,卖家VFP受理”

今天给你答案,可以!

为了完成网页版和客户端通讯,我去网上搜索了一大堆资料,看得头晕目眩,发现我的foxsocket类无法在网页版运行,只能用web编程中常用的websocket。。。

也就是说vfp服务端用foxsocket,客户端用网页,为了能给接收websocket的讯息,得了解它的通讯规范,foxsocket必须要做适应性改进。。。

经过2天的不断尝试,终于成功,现在分享给大家~~~

有一个遗憾,没考虑中文解码的问题,网页版向服务端发中文的时候会乱码,只有下次改进了。。。

vfp服务端截图:
图片附件: 游客没有浏览图片的权限,请 登录注册


网页端截图
图片附件: 游客没有浏览图片的权限,请 登录注册


主要的类foxsocket代码如下:
程序代码:
**************************************************
*-- 类:           foxsocket (d:\documents\visual foxpro 项目\foxweb.vcx)
*-- 父类:  custom
*-- 基类:    custom
*-- 时间戳:   10/18/25 01:56:02 PM
*
DEFINE CLASS foxsocket AS custom


    Height = 15
    Width = 68
    *-- 版本号
    ver = ""
    *-- 描述
    description = ""
    *-- 负责监听的套接字
    listensocket = 0
    *-- 当前套接字句柄
    sockethandle = 0
    *-- 收到的字节数
    bytesreceived = 0
    *-- 远程主机
    remotehost = ""
    *-- 远程ip地址
    remoteip = ""
    *-- 远程端口
    remoteport = 0
    *-- 本地主机名
    localhost = ""
    *-- 本地ip地址
    localip = ""
    *-- 本机端口
    localport = 0
    *-- 套接字连接协议。1表示TCP,2表示UDP,其他待开发
    protocol = 1
    *-- 当前连接状态
    state = 0
    Name = "foxsocket"


    *-- 连接远方主机方法及连接事件
    PROCEDURE connect
        LPARAMETERS remotehost,remoteport

        *!*    connect带参数时为方法,不带参数时为事件
        *********************************************
        *子类connect事件中调用父类的方法:
        *if PCOUNT()=0 &&定义没有参数时的行为
        *    input your code here
        *ELSE
        *    DODEFAULT(remotehost,remoteport) &&不能漏掉参数
        *ENDIF  
        *********************************************

        IF PCOUNT()=2 &&带参数则发出连接请求
            LOCAL n,stsockaddr
            WSAAsyncSelect(this.Sockethandle,thisform.hwnd,1124,51)    && FD_CONNECT or FD_READ or FD_CLOSE or FD_WRITE
            stSockAddr = BINTOC(2, "2RS");                   && sin_family = AF_INET
                       + BINTOC(htons(remotePort), "2RS");        && sin_port
                       + BINTOC(inet_addr(remotehost), "4RS");    && sin_addr
                       + REPLICATE(0h00, 8)
            n=connect(this.Sockethandle, @stSockAddr, LEN(stSockAddr))    && SOCKET_ERROR
            IF n#-1
                this.state=6
            ENDIF 
            RETURN n#-1
        ENDIF
    ENDPROC


    *-- winapi函数申明
    PROTECTED PROCEDURE declare
        * Socket初始化相关
        DECLARE INTEGER WSAGetLastError IN ws2_32
        DECLARE INTEGER WSAStartup         IN ws2_32 INTEGER wVersionRequested, STRING @lpWSAData
        DECLARE INTEGER WSACleanup         IN ws2_32

        * 基础Socket函数
        DECLARE INTEGER socket         IN ws2_32 INTEGER af, INTEGER type, INTEGER protocol
        DECLARE INTEGER closesocket IN ws2_32 INTEGER s
        DECLARE INTEGER bind         IN ws2_32 as _bind INTEGER s, STRING @name, INTEGER namelen
        DECLARE INTEGER listen         IN ws2_32 INTEGER s, INTEGER backlog
        DECLARE INTEGER accept         IN ws2_32 INTEGER s, STRING @addr, INTEGER @addrlen
        DECLARE INTEGER connect     IN ws2_32 INTEGER s, STRING @name, INTEGER namelen

        * 数据传输函数
        DECLARE INTEGER send         IN ws2_32 INTEGER s, STRING @buf, INTEGER len, INTEGER flags
        DECLARE INTEGER recv         IN ws2_32 INTEGER s, STRING @buf, INTEGER len, INTEGER flags
        DECLARE INTEGER sendto         IN ws2_32 INTEGER s, STRING @buf, INTEGER len, INTEGER flags, STRING @to, INTEGER tolen
        DECLARE INTEGER recvfrom     IN ws2_32 INTEGER s, STRING @buf, INTEGER len, INTEGER flags, STRING @from, INTEGER @fromlen

        * 地址转换函数
        DECLARE INTEGER inet_addr     IN ws2_32 STRING cp
        DECLARE STRING inet_ntoa     IN ws2_32 INTEGER in

        * 主机信息函数
        DECLARE INTEGER gethostname     IN ws2_32 STRING @name, INTEGER namelen
        DECLARE INTEGER gethostbyname     IN ws2_32 STRING name
        DECLARE INTEGER gethostbyaddr     IN ws2_32 STRING @addr, INTEGER len, INTEGER type

        * Socket选项控制
        DECLARE INTEGER getsockopt IN ws2_32 INTEGER s, INTEGER level, INTEGER optname, STRING @optval, INTEGER @optlen
        DECLARE INTEGER setsockopt IN ws2_32 INTEGER s, INTEGER level, INTEGER optname, STRING @optval, INTEGER optlen

        * IO控制函数
        DECLARE INTEGER ioctlsocket IN ws2_32 INTEGER s, INTEGER cmd, INTEGER @argp

        * 异步Socket函数
        DECLARE INTEGER WSAAsyncSelect IN ws2_32 INTEGER s, INTEGER hWnd, INTEGER wMsg, INTEGER lEvent

        * 其他实用函数
        DECLARE SHORT htons IN ws2_32 SHORT hostshort
        DECLARE SHORT ntohs IN ws2_32 SHORT netshort
        DECLARE INTEGER htonl IN ws2_32 INTEGER hostlong
        DECLARE INTEGER ntohl IN ws2_32 INTEGER netlong
        DECLARE INTEGER RtlMoveMemory IN kernel32 AS CopyMemory STRING @Dest, INTEGER Src, INTEGER nLength

        * 使用Windows CryptoAPI计算SHA1    
        DECLARE INTEGER CryptAcquireContext IN advapi32 ;        
                INTEGER @hProv, STRING cContainer, STRING cProvider, ;        
                INTEGER dwProvType, INTEGER dwFlags        
        DECLARE INTEGER CryptCreateHash IN advapi32 ;        
                INTEGER hProv, INTEGER Algid, INTEGER hKey, ;        
                INTEGER dwFlags, INTEGER @hHash        
        DECLARE INTEGER CryptHashData IN advapi32 ;        
                INTEGER hHash, STRING @pbData, INTEGER dwDataLen, ;        
                INTEGER dwFlags        
        DECLARE INTEGER CryptGetHashParam IN advapi32 ;        
                INTEGER hHash, INTEGER dwParam, STRING @pbData, ;        
                INTEGER @pdwDataLen, INTEGER dwFlags        
        DECLARE INTEGER CryptDestroyHash IN advapi32 INTEGER hHash    
        DECLARE INTEGER CryptReleaseContext IN advapi32 INTEGER hProv, INTEGER dwFlags    
        DECLARE INTEGER CryptBinaryToString IN crypt32 ;       
                STRING pbBinary, INTEGER cbBinary, INTEGER dwFlags, ;        
                STRING @pszString, INTEGER @pcchString
    ENDPROC


    *-- 关闭套接字方法及事件
    PROCEDURE close
        LPARAMETERS hsocket
        *!*    close带参数时为方法,不带参数时为事件
        IF PCOUNT()=1
            closesocket(hSocket)
            this.sockethandle=0
            this.state=0
        ENDIF
    ENDPROC


    *-- 当远方客户端企图连接时发生
    PROCEDURE connectionrequest
        LPARAMETERS requestid
        *!* 收到连接请求时自动产生requestid,同意请求则调用accept()并传递requestid
        *!* 若要拒绝连接,只需传递参数0给accept()方法
        this.accept(requestid)
    ENDPROC


    *-- 当收到远方电脑发送的数据时发生
    PROCEDURE dataarrival
        LPARAMETERS bytestotal
        *!*    dataarrival带参数时为方法,不带参数时为事件
        *!*    可以在这个方法中调用getdata()方法来获取数据
    ENDPROC


    *-- 发送数据期间发生
    PROCEDURE sendprocess
        LPARAMETERS bytessent,bytesremaining
    ENDPROC


    *-- 接收一个连接请求
    PROCEDURE accept
        LPARAMETERS requestid
        #DEFINE NACCEPT 6
        #DEFINE NTIMEOUT -1

        *!* 在connectionrequest中调用本方法,根据条件判断是否接受连接请求
        LOCAL stSockAddr, nSize, nIP, szIP, nPort,n
        *!*    n=(DATE()-{^1970-01-01})*24*3600*1000+INT(SECONDS()*1000)-8*3600*1000 &&唯一时间戳

        *****winsock没有拒绝连接的函数,我们采用先接受再关闭以达到关闭效果*****
        stSockAddr = REPLICATE(0h00, 16)
        nSize      = LEN(stSockAddr)
        this.sockethandle =accept(this.sockethandle, @stSockAddr, @nSize) &&新套接字用于通讯
        WSAAsyncSelect(this.sockethandle, thisform.hwnd, 1124, 33)  && FD_READ + FD_CLOSE=33 0x400+100=1124
        *WSAAsyncSelect(this.sockethandle, thisform.hwnd, 1124, 59)  && FD_READ + FD_CLOSE=33 0x400+100=1124
        *****也可以采用不响应connectionrequest请求的方式来拒绝连接,即请求端超时,但会造成客户端堵塞,不建议
        nIP   = CTOBIN(SUBSTR(stSockAddr, 5, 4), '4RS')
        *!*    SET MESSAGE TO TRANSFORM(nip)
        *szIP  = SYS(2600, inet_ntoa(nIP), 16)
        szip = inet_ntoa(nIP)
        szIP  = LEFT(szIP, AT(0h00, szIP)-1)
        nPort = ntohs(CTOBIN(SUBSTR(stSockAddr, 3, 2), '2RS'))
        this.remoteip=szip
        this.remoteport=nport
        this.state=7

        nAvail = 0
        ioctlsocket(this.sockethandle, 0x4004667F, @nAvail)
        this.bytesreceived=navail
        lcbuffer=REPLICATE(CHR(0),navail)
        this.getData(@lcbuffer,navail)

        ****************************************************
        *!*    ACTIVATE SCREEN && 激活屏幕,查看缓冲内容,调试用
        *!*    CLEAR
        *!*    ?this.getdata(@lcbuffer)
        *!*    ?lcbuffer
        ****************************************************
        IF this.bytesreceived>0

            * 检查是否为WebSocket升级请求        
            IF "Upgrade: websocket" $ lcbuffer AND "Connection: Upgrade" $ lcbuffer            
                * 提取Sec-WebSocket-Key            
                nPos = AT("Sec-WebSocket-Key:", lcbuffer)            
                IF nPos > 0                
                    cKey = SUBSTR(lcbuffer, nPos + 18)                
                    cKey = ALLTRIM(LEFT(cKey, AT(CHR(13), cKey)-1))                                
                    * 计算响应密钥                
                    cResponseKey = this.ws_accept_key(cKey)                                
                    * 发送HTTP 101响应                
                    cResponse = "HTTP/1.1 101 Switching Protocols" + CHR(13) + CHR(10) + ;                            
                    "Upgrade: websocket" + CHR(13) + CHR(10) + ;                            
                    "Connection: Upgrade" + CHR(13) + CHR(10) + ;                            
                    "Sec-WebSocket-Accept: " + cResponseKey + CHR(13) + CHR(10) + ;                            
                    CHR(13) + CHR(10)                                
                    nSent = send(this.sockethandle, @cResponse, LEN(cResponse), 0)                                
                    IF nSent > 0                    
                        *MESSAGEBOX("WebSocket连接已建立")                    
                        SET MESSAGE TO "WebSocket连接已建立..."
                        thisform.Edit1.value=thisform.edit1.value+"WebSocket发送连接报文如下..."+CHR(13)
                        thisform.Edit1.value=thisform.edit1.value+lcbuffer+CHR(13)
                        * 这里可以添加WebSocket消息处理逻辑 
                        thisform.Edit1.value=thisform.edit1.value+"WebSocket连接已建立..."+CHR(13)               
                    ENDIF            
                ENDIF        
            ELSE            
                * 不是WebSocket请求,返回普通HTTP响应            
                cResponse = "HTTP/1.1 200 OK" + CHR(13) + CHR(10) + ;                        
                "Content-Type: text/html" + CHR(13) + CHR(10) + ;                        
                "Connection: close" + CHR(13) + CHR(10) + ;                        
                CHR(13) + CHR(10) + ;                        
                "<html><body><h1>This is a WebSocket server</h1></body></html>"                        
                nSent = send(this.sockethandle, @cResponse, LEN(cResponse), 0)        
            ENDIF    
        ENDIF
    ENDPROC


    *-- 绑定套接字到指定的IP地址和端口
    PROCEDURE bind
        LPARAMETERS localip,localport
        LOCAL n,stsockaddr
        stSockAddr = BINTOC(2, '2RS');                   && sin_family ipv4
                   + BINTOC(htons(localPort), '2RS');        && sin_port
                   + BINTOC(inet_addr(@localip), '4RS');    && sin_addr
                   + REPLICATE(0h00, 8)

        n=_bind(this.Sockethandle, @stSockAddr, LEN(stSockAddr)) 

        RETURN n#-1
    ENDPROC


    *-- 检索来自远方计算机的数据
    PROCEDURE getdata
        LPARAMETERS cdata,nbytes &&cdata为输出型参数
        IF PCOUNT()=1
            nbytes=this.bytesreceived
        ENDIF
            n=recv(this.Sockethandle, @cdata, nbytes, 0)
        RETURN n 
    ENDPROC


    *-- 监听远方计算机的连接请求
    PROCEDURE listen
        #define        WM_SOCKET        0x400 + 100
        #define        FD_ACCEPT        8

        listen(this.Sockethandle, 5)
        WSAAsyncSelect(this.sockethandle, thisform.hWnd, WM_SOCKET, FD_ACCEPT) &&FD_ACCEPT=8
        this.state=2
        this.listensocket=this.sockethandle
    ENDPROC


    *-- 查看接收到的数据但不清空缓存
    PROCEDURE peekdata
        LPARAMETERS cdata,nbytes &&cdata为输出型参数
        *!*    本方法等同于getdata方法,但是不清除缓存。
        IF PCOUNT()=1
            nbytes=this.bytesreceived
        ENDIF

        RETURN  recv(this.Sockethandle, @cdata, nbytes, 2) &&MSG_PEEK    2
    ENDPROC


    *-- 向远方计算机发送数据
    PROCEDURE senddata
        LPARAMETERS cdata
        LOCAL n
            n=send(this.Sockethandle, @cdata, LEN(cdata), 0)
        RETURN n#-1
    ENDPROC


    *-- 处理自定义socket消息
    PROCEDURE msghandler
         LPARAMETERS hWnd, Msg, wParam, lParam

                IF this.sockethandle#wparam
                    this.sockethandle=wparam
                ENDIF 

                DO CASE
                    CASE lParam == 0x0008                && FD_ACCEPT      接收将要连接的通知
                        SET MESSAGE TO "远方电脑连接请求。。。"
                        requestid=(DATE()-{^1970-01-01})*24*3600*1000+INT(SECONDS()*1000)-8*3600*1000
                        this.connectionrequest(requestid) &&决定是否接受连接请求
                    
                    CASE lParam == 0x0001                && FD_READ        接收读准备好的通知   
                        nAvail = 0
                        ioctlsocket(this.sockethandle, 0x4004667F, @nAvail)  && 查询可读字节数
                        IF nAvail > 0
                            this.bytesreceived=nAvail
                            this.dataarrival(nAvail)                
                        ENDIF

                    
                    CASE lParam == 0x0002                && FD_WRITE       接收写准备好的通知 
                        this.readytosend()
                        &&可以发送数据时会产生此事件,没想好这里需要写什么代码
                    
                    CASE lParam == 0x0004                && FD_OOB         接收带边数据到达的通知
                        *MESSAGEBOX("待开发功能!") &&紧急通知,常用于终止,取消,比如下载
                    
                    CASE lParam == 0x0010                && FD_CONNECT     接收已连接好的通知
                        this.state=7
                        this.connect() &&当服务端接受连接时执行用户自定义连接事件
                    
                    CASE lParam == 0x0020                && FD_CLOSE       接收套接口关闭的通知
                        this.state=8
                        this.close(this.sockethandle) &&关闭套接字连接
                        this.close() &&执行用户响应的close事件
                    CASE lParam == 0x274D0010
                        *MESSAGEBOX('远程端口无响应')
                    OTHERWISE
                ENDCASE
    ENDPROC


    *-- 当发送操作完成后发生
    PROCEDURE sendcomplete
        *自定义发送完成事件
    ENDPROC


    *-- 获得本机名
    PROCEDURE getlocalhost
        RETURN STREXTRACT(SYS(0),"",SPACE(1))
    ENDPROC


    *-- 获得指定主机的ip地址
    PROCEDURE getipadress
        LPARAMETERS lchostname
        IF PCOUNT()=0 
            IF EMPTY(this.localhost)
                this.localhost=this.getlocalhost()
            ENDIF 
            lchostname=this.localhost
        ENDIF 

        * VFP示例:使用gethostbyname解析域名

        #DEFINE HOSTENT_SIZE 16

        * 解析域名
        lnHostEnt = gethostbyname(lcHostName)

        IF lnHostEnt = 0
            MESSAGEBOX("无法解析主机名" + lcHostName)
            lcip=""
        ENDIF

        * 读取hostent结构体
        lcHostEnt = REPLICATE(CHR(0), HOSTENT_SIZE)
        = CopyMemory(@lcHostEnt, lnHostEnt, HOSTENT_SIZE)

        * 获取IP地址
        lnIPAddr = CTOBIN(SUBSTR(lcHostEnt, 13, 4), "4rs")
        lcIPAddr = REPLICATE(CHR(0), 4)
        = CopyMemory(@lcIPAddr, lnIPAddr, 4)

        lnFirstIP = CTOBIN(SUBSTR(lcIPaddr, 1, 4), "4rs")
        lcIPAddr = REPLICATE(CHR(0), 4)
        = CopyMemory(@lcIPAddr, lnFirstIP, 4)

        * 转换为点分十进制
        lcIP = TRANSFORM(ASC(SUBSTR(lcIPAddr, 1, 1))) + "." + ;
               TRANSFORM(ASC(SUBSTR(lcIPAddr, 2, 1))) + "." + ;
               TRANSFORM(ASC(SUBSTR(lcIPAddr, 3, 1))) + "." + ;
               TRANSFORM(ASC(SUBSTR(lcIPAddr, 4, 1)))

        RETURN lcip
    ENDPROC


    *-- 建立一个套接字
    PROCEDURE createsocket
        LPARAMETERS nprotocol
        IF PCOUNT()=0 
            nprotocol=this.protocol
        ENDIF 
        this.sockethandle=socket(2,nprotocol,0) &&1,tcp协议;2,UDP协议
        BINDEVENT(thisform.hwnd, WM_SOCKET, this, "Msghandler")
        this.state=1
        RETURN this.sockethandle#0
    ENDPROC


    *-- 准备发送数据
    PROCEDURE readytosend
        *!* 准备发送数据,在此方法中调用senddata方法
    ENDPROC


    *-- 计算响应密钥,和websocket通讯专用
    PROCEDURE ws_accept_key
        PARAMETERS ckey
        LOCAL cMagic, cConcat, cSHA1, cBase64,nlen,hprov,hhash,nresult   
        cMagic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"    
        cConcat = cKey + cMagic        
        hProv = 0    
        hHash = 0    
        nResult = CryptAcquireContext(@hProv, 0, 0, 1, 0xF0000000)        
        IF nResult = 0        
            RETURN ""    
        ENDIF        
        nResult = CryptCreateHash(hProv, 0x8004, 0, 0, @hHash)        
        IF nResult = 0        
            CryptReleaseContext(hProv, 0)        
            RETURN ""    
        ENDIF        
        nResult = CryptHashData(hHash, cConcat, LEN(cConcat), 0)        
        IF nResult = 0        
            CryptDestroyHash(hHash)        
            CryptReleaseContext(hProv, 0)        
            RETURN ""    
        ENDIF        
        nLen = 20    
        cSHA1 = SPACE(nLen)    
        nResult = CryptGetHashParam(hHash, 2, @cSHA1, @nLen, 0)        
        CryptDestroyHash(hHash)    
        CryptReleaseContext(hProv, 0)        
        IF nResult = 0        
            RETURN ""    
        ENDIF        
        * Base64编码    
             
        nLen = 0    
        nResult = CryptBinaryToString(cSHA1, 20, 1, 0, @nLen)        
        IF nResult = 0        
            RETURN ""    
        ENDIF        
        cBase64 = SPACE(nLen)    
        nResult = CryptBinaryToString(cSHA1, 20, 1, @cBase64, @nLen)        
        IF nResult = 0        
            RETURN ""    
        ENDIF        
        cBase64 = LEFT(cBase64, nLen-1)  && 去掉末尾的null字符        
        RETURN cBase64
    ENDPROC


    PROCEDURE Error
        LPARAMETERS nError, cMethod, nLine
        LOCAL cprogram,cmsg,ccode
            cprogram=PROGRAM()
            cmsg=MESSAGE()
            ccode=MESSAGE(1)
            szMsg = '错误信息: ' + cMsg                  + 0h0D0D;
                  + '错误编号: ' + TRANSFORM(nError)     + 0h0D0D;
                  + '错误代码: ' + ccode                 + 0h0D0D;
                  + '出错程序: ' + cProgram              + 0h0D0D;
                  + '出错方法: ' + cMethod               + 0h0D0D;      
                  + '出错行号: ' + TRANSFORM(nLine)
            *MESSAGEBOX(szMsg, 2+48+512, "Error")
            =STRTOFILE(TRANSFORM(DATETIME())+ 0h0D0D+szmsg++ 0h0D0D,"foxsocket_error.log",1)
            MODIFY FILE foxsocket_error.log NOWAIT 
    ENDPROC


    PROCEDURE Init
        #define        WM_SOCKET    0x400+100
        LOCAL nResult
        IF TYPE("thisform")#"O"
            *MESSAGEBOX("foxsocket只能在表单中实例化!")
            RETURN .f.
        ELSE 
            this.declare()
            WSADATA=REPLICATE(CHR(0),398)
            nResult = WSAStartup(0x202, @WSADATA)
            this.ver=TRANSFORM(ASC(SUBSTR(wsadata,2,1)))+"."+TRANSFORM(ASC(SUBSTR(wsadata,1,1)))
            this.description=STRTRAN(SUBSTR(wsadata,5,257),CHR(0),"")
        ENDIF 
        RETURN (nResult = 0)
    ENDPROC


    PROCEDURE Destroy
        IF this.sockethandle>0
            *事实上可能有多个客户端连接存在,应
            this.close(this.sockethandle)
        ENDIF

        IF this.listensocket>0
            this.close(this.listensocket)
            *监听情况下,可能有多个客户端连接存在,应逐一关闭连接
            *可以在socket类里设置一个数组用来保存所有客户端的连接
        ENDIF 

        =wsacleanup()
    ENDPROC


ENDDEFINE
*
*-- EndDefine: foxsocket
**************************************************




[此贴子已经被作者于2025-10-18 14:30编辑过]

搜索更多相关主题的帖子: INTEGER 连接 this STRING if 
昨天 14:22
sam_jiang
Rank: 10Rank: 10Rank: 10
等 级:贵宾
威 望:14
帖 子:1017
专家分:1535
注 册:2021-10-13
收藏
得分:0 
vfp客户端代码如下:
程序代码:
**************************************************
*-- Form:         form1 (d:\documents\visual foxpro 项目\foxwebsocket_demo.scx)
*-- 父类:  form
*-- 基类:    form
*-- 时间戳:   10/18/25 02:04:09 PM
*
DEFINE CLASS form1 AS form


    Height = 417
    Width = 590
    DoCreate = .T.
    AutoCenter = .T.
    Caption = "Foxwebsocket 服务端演示demo"
    Name = "Form1"


    ADD OBJECT label1 AS label WITH ;
        AutoSize = .T., ;
        Caption = "IP 地址:", ;
        Height = 16, ;
        Left = 13, ;
        Top = 47, ;
        Width = 56, ;
        TabIndex = 1, ;
        Name = "Label1"


    ADD OBJECT label2 AS label WITH ;
        AutoSize = .T., ;
        Caption = "端 口: ", ;
        Height = 16, ;
        Left = 199, ;
        Top = 47, ;
        Width = 50, ;
        TabIndex = 2, ;
        Name = "Label2"


    ADD OBJECT edit1 AS editbox WITH ;
        Height = 288, ;
        Left = 12, ;
        TabIndex = 5, ;
        Top = 72, ;
        Width = 564, ;
        Name = "Edit1"


    ADD OBJECT edit2 AS editbox WITH ;
        Height = 50, ;
        Left = 12, ;
        TabIndex = 6, ;
        Top = 365, ;
        Width = 504, ;
        Name = "Edit2"


    ADD OBJECT label3 AS label WITH ;
        Caption = (chr(13)+ "这是一个Foxwebsocket 服务端的演示程序..."), ;
        Height = 37, ;
        Left = 12, ;
        Top = 0, ;
        Width = 565, ;
        TabIndex = 9, ;
        Name = "Label3"


    ADD OBJECT foxsocket1 AS foxsocket WITH ;
        Top = 48, ;
        Left = 528, ;
        Name = "Foxsocket1"


    ADD OBJECT text1 AS textbox WITH ;
        Alignment = 3, ;
        Value = 8888, ;
        Height = 20, ;
        InputMask = "9999", ;
        Left = 257, ;
        TabIndex = 3, ;
        Top = 44, ;
        Width = 50, ;
        Name = "Text1"


    ADD OBJECT command1 AS commandbutton WITH ;
        AutoSize = .T., ;
        Top = 42, ;
        Left = 387, ;
        Height = 25, ;
        Width = 60, ;
        Caption = "启动服务", ;
        TabIndex = 4, ;
        Name = "Command1"


    ADD OBJECT command2 AS commandbutton WITH ;
        Top = 387, ;
        Left = 524, ;
        Height = 25, ;
        Width = 60, ;
        Caption = "发  送", ;
        TabIndex = 7, ;
        Name = "Command2"


    ADD OBJECT txt_ip1 AS txt_ip WITH ;
        Left = 81, ;
        TabIndex = 8, ;
        Top = 43, ;
        Name = "Txt_ip1"


    PROCEDURE foxsocket1.dataarrival
        LPARAMETERS bytestotal
        LOCAL lcbuffer,n,nmark1,j,cencode,amark[4]
        lcbuffer=REPLICATE(CHR(0),this.bytesreceived)
        n=this.getdata(@lcbuffer)
        lcbuffer=SUBSTR(lcbuffer,1,n)

        IF SUBSTR(lcbuffer,1,1)=CHR(0x81) &&websocket 特征码
            IF ASC(SUBSTR(lcbuffer,2,1))>128 && 有随机掩码,需解码
                amark[1]=ASC(SUBSTR(lcbuffer,3,1))
                amark[2]=ASC(SUBSTR(lcbuffer,4,1))
                amark[3]=ASC(SUBSTR(lcbuffer,5,1))
                amark[4]=ASC(SUBSTR(lcbuffer,6,1))
                *nstartencode=7
                cencode=""
                FOR i=1 TO n-6
                    j=IIF(MOD(i,4)=0,4,MOD(i,4))
                    cencode=cencode+CHR(BITXOR(ASC(SUBSTR(lcbuffer,i+6,1)),amark[j]))
                ENDFOR
                lcbuffer=cencode
            ENDIF
        ENDIF 
        thisform .edit1.value=thisform.edit1.Value+"收到客户端发来讯息。。。"+CHR(13)
        thisform .edit1.value=thisform.edit1.Value+lcbuffer+CHR(13)


    ENDPROC


    PROCEDURE command1.Click
        thisform.edit1.Value=thisform.edit1.Value+"初始化foxsocket..."+CHR(13)
        thisform.foxsocket1.createsocket()
        thisform.edit1.Value=thisform.edit1.Value+"正在绑定"+TRANSFORM(thisform.text1.Value)+"端口..."+CHR(13)
        thisform.foxsocket1.bind(thisform.txt_ip1.Value,thisform.text1.Value)
        thisform.edit1.Value=thisform.edit1.Value+"进入监听状态..."+CHR(13)
        thisform.foxsocket1.listen()
        IF thisform.foxsocket1.state=2
            SET MESSAGE TO "服务已就绪。。。"
        ENDIF
        ="已启动"
        =.f.
    ENDPROC


    PROCEDURE command2.Click
        IF !EMPTY(thisform.edit2.value)
            cResponse = CHR(0x81)+CHR(LEN(ALLTRIM(thisform.edit2.Value)))+ALLTRIM(thisform.edit2.Value)
            thisform.foxsocket1.senddata(cresponse)
            thisform.edit1.Value=thisform.edit1.Value+"发送给客户端以下文字:"+CHR(13)
            thisform.edit1.Value=thisform.edit1.Value+thisform.edit2.Value+CHR(13)
            thisform.edit2.Value=""
        ENDIF 
        &&"Connection: close" + CHR(13) + CHR(10) + ;                        
        *    CHR(13) + CHR(10) + ;
    ENDPROC
    ENDPROC


    PROCEDURE txt_ip1.Init
        lchostname=thisform.foxsocket1.getlocalhost()
        this.Value=thisform.foxsocket1.getipadress(lchostname)
    ENDPROC


ENDDEFINE
*
*-- EndDefine: form1
**************************************************
昨天 14:24
sam_jiang
Rank: 10Rank: 10Rank: 10
等 级:贵宾
威 望:14
帖 子:1017
专家分:1535
注 册:2021-10-13
收藏
得分:0 
网页版客户端代码如下:
程序代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>网页版微信</title>
    <style>
        body {
            font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
            margin: 0;
            padding: 0;
            background-color: #f5f5f5;
            height: 100vh;
            display: flex;
        }
        .sidebar {
            width: 80px;
            background-color: #2e2e2e;
            height: 100%;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding-top: 20px;
        }
        .chat-container {
            flex: 1;
            display: flex;
            flex-direction: column;
            background-color: #eee;
        }
        .chat-header {
            height: 60px;
            background-color: #ededed;
            border-bottom: 1px solid #d6d6d6;
            display: flex;
            align-items: center;
            padding: 0 20px;
        }
        .chat-messages {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
        }
        .message {
            margin-bottom: 15px;
            display: flex;
            flex-direction: column;
        }
        .message-content {
            max-width: 60%;
            padding: 10px 15px;
            border-radius: 5px;
            margin: 5px 0;
            word-wrap: break-word;
        }
        .received {
            align-items: flex-start;
        }
        .received .message-content {
            background-color: white;
        }
        .sent {
            align-items: flex-end;
        }
        .sent .message-content {
            background-color: #95ec69;
        }
        .chat-input {
            height: 80px;
            background-color: #f5f5f5;
            border-top: 1px solid #d6d6d6;
            display: flex;
            padding: 10px;
        }
        .input-area {
            flex: 1;
            border: 1px solid #d6d6d6;
            border-radius: 5px;
            padding: 10px;
            resize: none;
            outline: none;
            font-size: 16px;
        }
        .send-btn {
            width: 80px;
            margin-left: 10px;
            background-color: #07c160;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
        }
        .avatar {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            background-color: #ccc;
            margin-bottom: 20px;
            cursor: pointer;
        }
        .status {
            color: white;
            text-align: center;
            margin-top: 10px;
            font-size: 12px;
        }
    </style>
</head>
<body>
    <div class="sidebar">
        <div class="avatar"></div>
        <div class="status">连接中...</div>
    </div>
    <div class="chat-container">
        <div class="chat-header">
            <h3>微信聊天</h3>
        </div>
        <div class="chat-messages" id="messages">
            <!-- 消息将在这里动态添加 -->
        </div>
        <div class="chat-input">
            <textarea class="input-area" id="messageInput" placeholder="输入消息..."></textarea>
            <button class="send-btn" id="sendBtn">发送</button>
        </div>
    </div>

    <script>
        const messagesContainer = document.getElementById('messages');
        const messageInput = document.getElementById('messageInput');
        const sendBtn = document.getElementById('sendBtn');
        const statusElement = document.querySelector('.status');
        
        // WebSocket连接
        const socket = new WebSocket('ws://localhost:8888');
        
        socket.onopen = function(e) {
            statusElement.textContent = '已连接';
            statusElement.style.color = '#07c160';
            addMessage('系统', '已连接到服务器', 'received');
        };
        
        socket.onclose = function(e) {
            statusElement.textContent = '已断开';
            statusElement.style.color = 'red';
            addMessage('系统', '与服务器的连接已断开', 'received');
        };
        
        socket.onerror = function(error) {
            statusElement.textContent = '连接错误';
            statusElement.style.color = 'red';
            addMessage('系统', '连接发生错误: ' + error.message, 'received');
        };
        
        socket.onmessage = function(event) {
            addMessage('对方', event.data, 'received');
        };
        
        sendBtn.addEventListener('click', sendMessage);
        messageInput.addEventListener('keypress', function(e) {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                sendMessage();
            }
        });
        
        function sendMessage() {
            const message = messageInput.value.trim();
            if (message) {
                socket.send(message);
                addMessage('', message, 'sent');
                messageInput.value = '';
            }
        }
        
        function addMessage(sender, text, type) {
            const messageElement = document.createElement('div');
            messageElement.className = `message ${type}`;
            
            const senderElement = document.createElement('span');
            senderElement.textContent = sender;
            senderElement.style.fontSize = '12px';
            senderElement.style.color = '#888';
            senderElement.style.margin = '0 5px';
            
            const contentElement = document.createElement('div');
            contentElement.className = 'message-content';
            contentElement.textContent = text;
            
            messageElement.appendChild(senderElement);
            messageElement.appendChild(contentElement);
            messagesContainer.appendChild(messageElement);
            
            // 滚动到底部
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        }
    </script>
</body>
</html>
昨天 14:26
sych
Rank: 7Rank: 7Rank: 7
等 级:黑侠
威 望:7
帖 子:446
专家分:695
注 册:2019-10-11
收藏
得分:0 
非常棒,辛苦了
昨天 15:51
schtg
Rank: 13Rank: 13Rank: 13Rank: 13
来 自:Usa
等 级:贵宾
威 望:67
帖 子:2280
专家分:4768
注 册:2012-2-29
收藏
得分:0 
昨天 17:04
快速回复:增强版的foxsocket来了
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.044158 second(s), 11 queries.
Copyright©2004-2025, BC-CN.NET, All Rights Reserved