优秀作品

# 例程9:TCP文件传输程序——服务器端,与例程10构成一对

# 客户端申请下载finecomp.exe二进制文件,服务器端读出该文件的前300字节内容,发送给客户端


count = 0                           # 记录在线人数

os = OS()                            # 定义操作系统对象

time = TIME()                     # 定义TIME对象time


def serverFunc(list)            # 定义线程函数

{

    count = count + 1

    print("在线人数:%\n",count)

    clientnet  = list[0]                                  # 拆包线程参数,列表第一项是客户端的NET对象clientnet

    clientaddr = list[1]                                 # 列表第二项是客户端的IP地址

    # 服务器监听到客户端接入后,给客户端发送信息响应

    buf = "请发送文件名,包含扩展名!"        # 响应信息内容 


    sendnet = clientnet.sendNet()              # 定制单次发送的NET对象

    sendnet.send(buf,len(buf))                    # 基于定制NET对象,启动发送(发送长度不允许超过1024字节,如果超出,需要切片发送)

    while sendnet.sendOK() != -1  {PowerDown(3)}   # 等待发送函数退出(-1表示退出),退出原因:有超时退出和发送完成退出

    xsend = sendnet.sendRead()                # 读出发送结果

    if xsend == -1                                        # 超时

    {

       count = count - 1                               # 在线人数计数器 -1

       print("在线人数:%\n",count)

       clientnet.close()                                   # 关闭clientnet

       return                                                  # 结束文件服务线程

    }       


    # 服务器发送完应答信息后,转而等待客户端发送过来的文件名(包含扩展名),

    # 该等待时间不确定,如果10分钟内没有收到文件名,则,结束线程。

    timescount = 0      # 计数器用于控制服务器超时退出伺服。

    while True

    {

        recvnet = clientnet.recvNet()                # 定制本次接收的NET对象

        recvnet.recv()                                         # 基于定制NET对象,启动接收

        while recvnet.recvOK() != -1  {PowerDown(3)}      # 等待接收函数退出(退出原因:超时、货收到信息),释放资源。

         #接收结果是一个列表,第一项是-1表示接收超时,否则表示收到的数据包字节数,第二项是收到的数据(最大不超过1024字节)

        xrecv = recvnet.recvRead("string")                   # 读取接收结果

        if xrecv[0] == -1            # 在初始化时我们设置的tiemout = 3000毫秒,系统自动重新连接5次,每次超时时间大约15秒

        {

           timescount = timescount + 1            # 每超时一次计数器(timescount)+1

           if timescount > 40                              # 40*15 = 600秒,即10分钟,如果如果10分钟,结束对当前客户端的伺服服务。

           {

              count = count - 1                            # 在线人数计数器 -1

              print("在线人数:%\n",count)

              clientnet.close()                               # 关闭clientnet

              return                                              # 结束文件服务线程  

           }

           continue                                             # 没有超过设置时间,继续接收

        }  

        recvbuf = xrecv[1]                                # 获取接收到的数据

        break

    }


    # 解析收到的信息

    if recvbuf != "exit"                                   # 如果收到的不是"exit",应该是文件名

    {

        fp = FILEOPEN(recvbuf,"rb")                 # 收到文件名后,服务器尝试打开文件

        if fp == False                                       # 如果文件打不开,通知客户端,然后结束线程

        {

            fp.close()

            buf = "打开文件失败,请检查文件名、或路径是否错误!"

            print("%\n",buf)                            # 报错  

            sendnet = clientnet.sendNet()                # 定制发送NET对象

            sendnet.send(buf,len(buf))                  # 启动发送,返回资源ID号

            while sendnet.sendOK() != -1  {PowerDown(3)}    # 等待发送函数退出(-1表示退出),释放资源

            xsend = sendnet.sendRead()                # 读取发送结果

            if xsend == -1                                        # 超时,网络有问题

            {

               count = count - 1                               # 在线人数计数器 -1

               print("在线人数:%\n",count)

               clientnet.close()                                   # 关闭clientnet

               return                                                  # 结束文件服务线程 

            }  

        }

        else    # 正确打开文件,准备发送文件

        {

            data = fp.read(200)                     # 读出300个字节的二进制文件

            fp.close()                              # 关闭文件

            # 先发送通知:客户端收到该通知后,切换到二进制数据接收模式

            sendnet = clientnet.sendNet()           # 定制发送NET对象

            sendnet.send("binary",6)                # 转向二进制接收的通知

            while sendnet.sendOK() != -1  {PowerDown(3)}    # 检测发送函数直到退出,释放资源

            xsend = sendnet.sendRead()                                  # 读取发送结果

            if xsend == -1                                            # 超时,结束服务。

            {

               count = count - 1                                   # 在线人数计数器 -1

               print("在线人数:%\n",count)

               clientnet.close()                                      # 关闭clientnet

               return                                                      # 结束文件服务线程 

            }

            time.sleep(500)

          

            # 发送二进制发送数据

            sendnet = clientnet.sendNet()                     # 定制发送NET对象

            sendnet.send(data,200)                            # 基于定制NET对象,启动发送

            while sendnet.sendOK() != -1  {PowerDown(3)}    # 检测发送函数直到退出,释放资源

            xsend = sendnet.sendRead()                                  # 读取发送结果

            if xsend == -1                                            # 超时,结束服务。

            {

               count = count - 1                                   # 在线人数计数器 -1

               print("在线人数:%\n",count)

               clientnet.close()                                      # 关闭clientnet

               return                                                      # 结束文件服务线程 

            }

            time.sleep(500)

            

            # 至此,文件发送结束,再发送"exit",通知客户端,文件全部发送完毕。

            sendnet = clientnet.sendNet()              # 定制发送NET对象

            sendnet.send("exit",4)                            # 基于定制对象,启动发送

            while sendnet.sendOK() != -1  {PowerDown(3)}     # 检测发送函数直到退出,释放资源

            xsend = sendnet.sendRead()                 # 读取发送结果

            if xsend == -1                                         # 超时,结束本次连接

            {

               count = count - 1                                # 在线人数计数器 -1

               print("在线人数:%\n",count)

               clientnet.close()                                   # 关闭clientnet

               return                                                   # 结束文件服务线程 

            }

        }

    }


    # 至此,要么是客户端主动下线了,要么是申请的文件全部发送结束了

    count = count - 1                                            # 在线人数计数器 -1

    print("在线人数:%\n",count)                                     

    clientnet.close()                                               # 关闭clientnet,定制的NET对象,系统会自动回收

    # 线程执行完毕,自动结束线程

}


server_addr = "192.168.111.195"                 # 服务器地址

port = 5500                                                 # 服务端口号


net = NET()                                                  # 定义NET对象net

net = net.socket(net.IPv4,net.STREAM,net.TCP)  # 创建socket,并赋值net(IPv4、STREAM数据流、TCP协议)

net.bind(net.IPv4,server_addr,port)            # 将服务器:地址簇、地址和端口号与net绑定

net.timeout(3000)                                       # 设置超时时间3000毫秒

net.listen(10)                                               # 将net设置为监听状态,参数10是最大并发客户端数量

while True

{

    print("服务器伺服接收!\n")

    net.accept()                                                         # 开始监听,net.timeout()设置对accept有效

    while net.acceptOK() != -1 {PowerDown(3)}      # 等待退出监听函数(返回-1表示退出accept函数)

    #读出的监测结果是一个列表,列表第一项是-1表明超时,如果不是-1,表明监测到客户接入,

    #列表第二项是客户端的net,第三项是客户端地址

    xaccept = net.acceptRead()                      # 读出结果是一个列表

    if xaccept[0] == -1 {continue}                   # 超时,继续监测

    list = [xaccept[1],xaccept[2]]                     # 读出监听到的客户端的net和客户端地址,打包成一个列表

    thread = THREAD()                                   # 定义线程对象

    thread = thread.CreateThread(serverFunc,list)     # 创建子线程处理该连接,列表作为线程参数

    thread.start()                                              # 启动子线程

}

net.clear()  #清除网络资源



# 例程10:TCP文件传输程序——客户端,与例程9构成一对


gui = GUI("fine")                          # 定义FIne窗口的GUI对象,用于图形界面显示

time = TIME()

#gui.HideConsoleWindow()         # 隐藏控制台窗口


serveraddr = "192.168.111.195"    # 服务器地址

port = 5500                                  # 该服务的端口号


net = NET()                                  # 定义一个NET对象

net = net.socket(net.IPv4,net.STREAM,net.TCP)     # 地址族:IPv4、数据流格式:STREAM、通讯协议:TCP

net.timeout(3000)                        # 设置net的接收和发送超时时间,connect()不受net.timeout()超时设置影响。


# 连接服务器

count = 0

box = GUI("box")                          # 创建MessageBox窗口对象

box.MessageBox("开始连接……","确定")

while box.MessageBoxClosed() != -1

{

    

    net.connect(net.IPv4,serveraddr,port)      # 参数是服务器端的:地址族、网址、端口号

    while net.connectOK() != -1                      

    {

        PowerDown(3)

        count = count + 1

        box.SendMessageBox("连接中:" + itoc(count))    # 显示连接中提示

    }

    # 至此,表明以退出连接状态

    box.CloseMessageBox()                   # 关闭box窗口


    x = net.connectRead()                     # 读取连接的结果               

    if x == -1                                          # 如果连接不成功,创建嵌套MessageBox提示。

    {

        box1 = GUI("box")                        # 创建MessageBox嵌套(二级MessageBox窗口)

        box1.MessageBox("未能连接到服务器,将退出程序!","确定")

        while box1.MessageBoxClosed() != -1 {PowerDown(3)}

        return                             # 退出主线程(退出程序)

    }

}


# 连接到服务器后,等待服务器应答

count = 0                            # 尝试接收次数

while True                           # 进入接收循环

{

    recvnet = net.recvNet()                       # 定制单次接收NET对象

    recvnet.recv()                                       # 基于定制对象启动接收

    while recvnet.recvOK() != -1 {PowerDown(3)}    # 等待接收函数退出

    xrecv =recvnet.recvRead("string")                   # 读取结果,释放资源

    if xrecv[0] == -1                                   # 超时(大约18秒)

    {

        box = GUI("box")                        # 创建MessageBox窗口

        box.MessageBox("超时!未接收到服务器应答,将退出程序!","确定")

        while box.MessageBoxClosed() != -1 {PowerDown(3)}

        return                             # 退出主线程(退出程序)          

    }

    recvbuf = xrecv[1]              # 接收到服务器应答,读取数据,并释放接收资源

    break                                   # 退出接收循环

}


# 设计Fine窗口,输入要下载的文件名

title   = "TCP文件传输——客户端"                    # 窗口标题

size    = [30,5,80,20]                                         # 窗口位置、尺寸


text    = ["服务器应答","text",12,3,62,1]            # 显示区域

edit    = ["文件名","edit","s",12,7,62,1]              # 录入文件名控件

button  = [" 确定 ","button",55,11,12,1]           # “确定”按钮


list    = [title,size,text,edit,button]                    # 打包窗口参数


gui.Fine(list)                                          # 创建Fine窗口,并返回资源ID号

gui.SendText([recvbuf])                       # 窗口创建后,稍微延迟一会再显示,否则显示不出来

filename = ""                                        # 初始化文件名为空

while gui.FineClosed() != -1                 # 检测文件录入窗口是否关闭

{

    PowerDown(3)                                 # 降低数据录入期间的功耗

    # 等待输入待下载文件名

    if gui.FineReady() == -1 {continue}     # 检测是否有数据录入(0有数据,-1无数据)

    x = gui.FineRead()                                # 读取数据,第一项是指令(button-0),第二项是文件名(对应edit控件)

    filename = x[1]                                     # 拆包,把文件名取出 

    if filename == "" 

    {

        # 弹窗提示错误

        box = GUI("box")                              # 创建MessageBox对象

        box.MessageBox("文件名不能为空!","确定")                     # 创建MessageBox窗口

        if box.MessageBoxClosed() != -1 {PowerDown(3)}           # 等待关闭窗口

        continue                                                                             # 跳转,等待下次数据录入

    }

    gui.CloseFine()                        # 获取文件名后,关闭文件名录入窗口

}


# 没有文件名,将退出程序。但是,由于已经建立了连接,退出程序前,需要通知服务器,以便服务器及时关闭对应的服务

if filename == ""                         

{

    buf = "exit"                                      # 客户端退出指令

    sendnet = net.sendNet()                # 定制单次发送的NET对象

    sendnet.send(buf,len(buf))             # 启动发送,并返回发送资源

    while sendnet.sendOK() != -1 {PowerDown(3)}    # 等待发送函数退出(返回-1表示发送函数退出),退出后,立即释放资源。

    net.close()                                        #清除网络资源

    return                                               # 退出程序

}


# 文件名录入成功后,向服务器发送文件名


sendnet = net.sendNet()                               # 定制单次发送NET对象

sendnet.send(filename,len(filename))           # 启动发送,并返回发送资源

while sendnet.sendOK() != -1 {PowerDown(3)}     # 等待发送函数退出(返回-1表示发送函数退出)

xsend = sendnet.sendRead()                         # 读发送结果,同时释放资源

if xsend == -1                                                 # 发送字节数为0,表明发送错误

{

   box = GUI("box")                                         # 创建MessageBox窗口对象

   box.MessageBox("发送文件名超时,将退出程序!","确定")         # 弹出提示信息

   while box.MessageBoxClosed() != -1 {PowerDown(3)}             # 等待关闭MessageBox窗口

   net.close()                                                     # 清除网络资源

   return                                                            # 程序返回,退出程序

}


recv_finished = 0                   # 文件接收完毕标志(0未接收完,1接收完毕)


# 设计Fine窗口,动态显示收到的数据

title       = "服务器下载文件接"

size       = [10,1,120,40]

menu    = ["文件","menu","保存"]

editbox = ["","editbox",0,0,116,36]

list        = [title,size,menu,editbox]


gui = GUI("fine")                                    # 创建Fine窗口对象(每次创建窗口都需要先创建窗口对象,一个窗口对象对应一个窗口)

gui.Fine(list)

while gui.FineClosed() != -1

{

   PowerDown(3)

   while not recv_finished                      # 如果文件未收完,持续循环直到文件接收完毕

   {

      # 冗余处理,检测窗口状态,如果用户中途关闭接收窗口,终止接收,关闭net,退出程序

      if gui.FineClosed() == -1   

      {

         net.close()                            # 清理网络资源

         box = GUI("box")                 # 创建MessageBox窗口对象,弹窗提示。

         box.MessageBox("客户端退出接收程序!","确定")              # 弹出提示信息

         while box.MessageBoxClosed() != -1 {PowerDown(3)}     # 等待关闭MessageBox窗口

         return

      }

      

      # 循环接收服务器端发送的数据包

      recvnet = net.recvNet()                       # 每次接收数据包之前,需要定制一个接收Net对象,基于此对象操作

      recvnet.recv()                                       # 启动接收

      while recvnet.recvOK() != -1 {PowerDown(3)}      # 等待接收过程结束,并释放资源

      xrecv = recvnet.recvRead("string")                                    # 读取结果

      if xrecv[0] == -1                   # 超时次数超限

      {

         box = GUI("box")              # 弹窗提示错误

         box.MessageBox("接收文件超时,将退出程序!","确定")       # 弹出提示信息

         while box.MessageBoxClosed() != -1 {PowerDown(3)}        # 等待关闭MessageBox窗口


         gui.CloseFine()                                  # 关闭fine窗口

         net.close()                                         # 关闭net,清理网络资源

         return                                                # 程序返回,退出程序

      }

      recvbuf = xrecv[1]                # 如果接收正确,获取数据

      if recvbuf == "exit"               # 收到的数据包为“exit”时,表明文件发送完毕、服务器已退出

      {

          recv_finished = 1              # 置位数据接收完毕标志

          box = GUI("box")              # 弹窗提示

          box.MessageBox("文件接收完毕!","确定")                         # 弹出提示信息

          while box.MessageBoxClosed() != -1 {PowerDown(3)}     # 等待关闭MessageBox窗口

          break                                                                                   # 退出发送循环 

      }

      elif recvbuf == "binary"   # 转向二进制接受模式

      {

          # 使用二进制接收模式,循环接收,直到收到"exit"发送结束标志,置recv_finished = 1,退出程序

          while True

          {

              recvnet = net.recvNet()                       # 每次接收数据包之前,需要定制一个接收Net对象,基于此对象操作

              recvnet.recv()                                       # 启动接收

              while recvnet.recvOK() != -1 {PowerDown(3)}      # 等待接收过程结束,并释放资源

              xrecv = recvnet.recvRead("binary")                                    # 读取结果

              if xrecv[0] == -1                   # 超时次数超限

              {

                 box = GUI("box")              # 弹窗提示错误

                 box.MessageBox("接收文件超时,将退出程序!","确定")       # 弹出提示信息

                 while box.MessageBoxClosed() != -1 {PowerDown(3)}        # 等待关闭MessageBox窗口


                 gui.CloseFine()                                  # 关闭fine窗口

                 net.close()                                         # 关闭net,清理网络资源

                 return                                                # 程序返回,退出程序

              }

              recvbuf = xrecv[1]                # 如果接收正确,获取数据

              if recvbuf == "exit"               # 收到的数据包为“exit”时,表明文件发送完毕、服务器已退出

              {

                  recv_finished = 1              # 置位数据接收完毕标志

                  box = GUI("box")              # 弹窗提示

                  box.MessageBox("文件接收完毕!","确定")                         # 弹出提示信息

                  while box.MessageBoxClosed() != -1 {PowerDown(3)}     # 等待关闭MessageBox窗口

                  break                                                                                   # 退出发送循环 

              }

              else      # 收到数据包

              {

                  str = sprint(recvbuf)

                  gui.SendEditbox([str])       # 将收到的数据显示在显示区

              }

          }

      }

      else      # 收到数据包

      {

          str = sprint(recvbuf)

          gui.SendEditbox([str])       # 将收到的数据显示在显示区

      }

   }

   

   # 数据接收完毕之前,程序无法流转到此处,按钮消息被阻滞(不响应按钮和菜单),之后才响应菜单或按钮

   if gui.FineReady() == 0                # 有界面数据输出

   {

      x = gui.FineRead()                     # x[0]是指令(menu-0-0来自menu),x[1]是文件内容(来自editbox)

      # 检查同名文件是否存在

      fp = FILEOPEN(filename,"r")      # 尝试打开文件

      if fp == False                              # 打开失败,表明文件不存在

      {

         is_writefile = 1                         # 置位写文件标志,可以写入文件标志

      }

      else                                             # 如果能打开文件,说明文件存在,询问是否同意覆盖原文                          

      {     

         fp.close()                                  # 关闭文件


         box = GUI("box")                     # 创建弹窗GUI对象

         box.MessageBox(filename+"已经存在,是否要覆盖原文件?","确定|取消")

         while box.MessageBoxClosed() != -1 {PowerDown(3)}          # 等待做出选择

         select = gui.MessageBoxRead(box)                                        # 读取选择结果

         if select == 0                                       # 0表明是确定

         {

               is_writefile = 1                               # 置位写文件标志,将写入文件 

         }

         else                                                      # 表明选择的是取消、或直接关闭弹窗

         {

               is_writefile = 0                               # 清0写文件标志,将不会写文件

         }

      }


      if is_writefile == 1                          # 如果写文件标志是1,进行写文件操作(将覆盖原文件)

      {

         fp = FILEOPEN(filename,"w")      # 只写方式打开文件,

         fp.write(x[1])                                # 将从editbox控件上读出的内容,写入文件

         fp.close()                                      # 关闭文件

      }

   }

}

net.close()  #清除网络资源


0