机考系统中的交/发卷设计
发表时间:2023-08-16 来源:明辉站整理相关软件相关文章人气:
[摘要]作者: 杨家成 大多数计算机考试需要进行机试(如计算机等级考试),在机试中会碰到如何给考生分发试卷及回收答卷等问题。一般的解决方案是: 采用软盘;在服务器上建立考生文件夹,设置登录密码或进行登录...
作者: 杨家成
大多数计算机考试需要进行机试(如计算机等级考试),在机试中会碰到如何给考生分发试卷及回收答卷等问题。一般的解决方案是: 采用软盘;在服务器上建立考生文件夹,设置登录密码或进行登录限制;基于文件夹共享,以文件复制方式实现发卷、交卷。这些方案在实施过程中都存在不少问题,如软盘的意外损坏; 工作量太大,操作太烦琐; 共享出来的交卷文件夹可能被考生识破而导致安全隐患等等。相比而言, 采用基于Winsock的文件传输方式进行发卷、交卷可以在一定程度上解决这个问题。实践证明,这是一个行之有效的方案,本文针对这一方案进行介绍。
编程思路
本系统的基本思路是: 在局域网环境下,考试前把各卷别的考试文件分别压缩,放在服务器的某个文件夹中,服务器端执行服务器程序,负责接收考生的请求并做出响应。各考生执行客户端程序,先通过其中的发卷模块向服务器请求把考试文件发送到考生的本地硬盘,考试完成后再通过其中的交卷模块把答案发送回服务器。由于文件的发送是基于TCP协议的端口通信,考生无从知道试题及答案放在哪里,很好地解决了安全问题。下面是本系统实现过程中的一些关键问题:
1. 多连接的实现
由于有多个考生参加考试,每个考生都要与服务器建立一个连接,为此在服务器程序中采用动态建立多连接,即采用Winsock控件数组。
2.数据类型
发送的数据类型通常有字串流(String)和字节流(Char数组)两种。前者一般用于发送辅助信息,如文件大小、考生信息等,后者用于发送文件正文。
3. 发送、接收结束的判断
每进行一次发送(或接收)后,将已发送(或已接收)的字节数与源文件的大小进行比较,判定是否结束。
4.文件夹的发送
Winsock控件只能发送文件,但考试中常常包含文件夹。怎么办?办法是先在程序中调用压缩软件将其压缩成单个文件再发送,接收方在接收完后进行解压缩还原。这是本系统的一个关键思路。
5.等待压缩/解压缩的完成
由于压缩/解压缩程序是通过VB的Shell函数调用执行的,而Shell是以异步方式执行压缩/解压缩程序的,这样就会出现没等压缩/解压缩完成,VB代码继续往下执行的情况。为此需要借助OpenProcess、GetExitCodeProcess这两个Windows API,通过循环读取进程状态值控制程序的等待。
6.注意事项
发送方在发送数据时采用间歇方式,否则易造成客户端数据丢失,通常的做法是执行VB的DoEvents语句。接收方在接收期间不能出现与用户进行交互的等待状态,否则会耽误接收,造成数据丢失。
服务器端接收考生交卷时,应接收完所有考生的压缩文件后,再统一进行解压缩,否则会加重服务器的负担。
7. 程序流程
客户端程序、服务器程序的流程图分别如图1、图2所示:
关键代码
1.建立保存考生信息的数据类型
先在服务器程序的标准模块中创建一个用于存放每个连接有关信息(每个连接对应一个考生)的自定义数据类型ClientConn:
Public Type ClientConn
Recvd As Boolean ’是否已接收过数据
FileNum As Integer ’文件号
FinishSize As Double ’完成收发的字节数
FileSize As Double ’文件的大小
Gh As String * 6 ’交卷者的考号
Jb As String * 1 ’交卷者的卷别
End Type
在服务器程序窗体模块中声明一个用于记录所有连接信息的模块级动态数组Conns:
Dim Conns() As ClientConn
2.服务器端建立连接:
Private Sub wisServer_ConnectionRequest(Index As Integer, ByVal requestID As Long)
If Index = 0 Then
Conncount = Conncount + 1
’连接总数,为模块级变量
Load wisServer(Conncount)
’服务器端WinSock控件数组
ReDim Preserve Conns(1 To Conncount)
wisServer(Conncount).LocalPort = 0
wisServer(Conncount).Accept requestID
End If
End Sub
3.发卷时服务器发送文件正文
Private Sub wisServer_SendComplete(Index As Integer)
Dim DataSize As Integer
Dim outFileData() As Byte
If Conns(Index).FinishSize < Conns(Index).FileSize Then ’此前文件已打开
DoEvents
If Conns(Index).FileSize - Loc(Conns(Index).FileNum) < MAXSENDSIZE Then
’MAXSENDSIZE是每次发送的最大字节数
DataSize = Conns(Index).FileSize - Loc(Conns(Index).FileNum)
Else
DataSize = MAXSENDSIZE
End If
ReDim outFileData(0 To DataSize - 1)
Get #Conns(Index).FileNum, , outFileData
wisServer(Index).SendData outFileData
Conns(Index).FinishSize = Conns(Index).FinishSize + DataSize
Else
Close (Conns(Index).FileNum)
End If
End Sub
说明:客户端程序的交卷模块代码与此类似。
4. 客户端接收数据代码
Private Sub wisClient_DataArrival(ByVal bytesTotal As Long)
Dim inData As String
’用于接收辅助信息
Dim inFileData() As Byte
’用于接收文件正文
Dim GetRarFile As String, unRarTarget As String ’文件路径
If Not Recvd Then ’如果是首次接收
wisClient.GetData inData, vbString
’接收文件的大小信息
FileSize = CDbl(Val(inData))
Recvd = True
Else ’开始接收文件正文
ReDim inFileData(0 To bytesTotal - 1)
wisClient.GetData inFileData, vbArray + vbByte
Put FileNum, , inFileData
’此前文件已建立并打开
RecvdSize = RecvdSize + bytesTotal
If RecvdSize = FileSize Then
’接收完毕
Close (FileNum)
wisClient.Close
’由接收方关闭连接而非发送方,否则会丢失数据
GetRarFile = GhDir & “\” & txtGh & “.rar”
’GhDir是考试目录,txtGh存放考号
unRarTarget = GhDir & “\”
Pid = Shell(App.path & “\rar x -inul” & GetRarFile & unRarTarget) ’解压缩
hProcess=OpenProcess(PROCESS_
QUERY_INFORMATION, 0, Pid)
’调用WinAPI
Do ’等待解压缩结束
Call GetExitCodeProcess(hProcess, ExitCode)
DoEvents
Loop While ExitCode = STILL_ALIVE
Call CloseHandle(hProcess)
Kill GhDir & “\” & txtGh & “.rar”
MsgBox “发卷完毕”
Shell “C:\Windows\explorer.exe/n,
/e,” & GhDir, vbMaximizedFocus
Unload Me
End If
End If
End Sub
说明:服务器程序中接收交卷的代码与此相似。
以上代码已在Windows 2000 Server、Windows XP、Windows 98平台下成功运行,并在实践中获得成功的应用。由于篇幅所限,在此没给出完整代码。
小 结
在本系统的编程中有两点值得注意: 一个是压缩-发送-解压缩的思路,另一个是用自定义数据类型数组保存考生(连接)的信息。当然,本系统只是解决了服务器与考生用机之间的安全问题,至于考生与考生之间通过文件夹共享互通答案,也许只有采取暂时的物理隔离手段才是最有效的解决办法。