完成端口,谁能帮我改一点点(300分) ( 积分: 300 )

  • 主题发起人 asksomeone
  • 开始时间
线程这个TRecvSendThread应该是发得,不是有三个单元吗?》
一个单元主要是处理LISTEN的线程TListenThread
另一个单元用于处理接收的线程TRecvSendThread
最后一个就是主程序调用了
以上仅仅是个人想法及猜测,请试之以考究
 
function TServerClientSocket.WorkBlock(var Block: PBlock; Transfered: DWORD): DWORD;
....
try
case Block^.Data.Event of
seRead:
begin
Self.Event(seRead);
DoRead(@Buffer, Transfered); //这里两句我觉得有点问题
if not PrepareRecv(Block) then
Result := RESPONSE_FAIL;
是不是该写成
if not PrepareRecv(Block) then
Result := RESPONSE_FAIL
else
DoRead(@Buffer, Transfered);
应该先收取数据后再DoRead来接收数据吧
 
楼上兄弟,我觉得PrepareRecv(Block)这个函数是判断数据收完了没有,不知道你是怎么想的呢?
 
PrepareRecv(Block) 是一个准备动作,将数据接收到Block中,
而DoRead(@Buffer, Transfered); 是一个接收Buffer消息通知,
所以应该放在后面
 
刚才我做了个测试,确实我的看法错了,请楼上楼下的不要见笑
 
提示一下:看懂了每一行代码,但不一定就理解了IOCP是个什么东西。
就像我懂二十六个字母,但不懂E文。
如果不能理解IOCP是个什么东西,有什么作用,怎么用,我觉得还是乖乖用阻塞+线程好了,像INDY。
 
AcceptEX和Accept能代换吗?
 
IOCP现在怎么这么吃香。
AcceptEX和Accept的实现机制是不一样的。
 
给你点提示:
客户端发消息到IOCP服务端一侧没什么说的, 但是返回分成两种情况就是无结果集和有结果集的情况, 分别用不同方法处理, 有结果集需要在两侧约定一个数据格式, 确定字段分隔符, 服务端分包返回结果集, 客户端接收全部结果集或前面部分结果集,给出调试方法的返回值(成功或几种失败)。 再由上层应用使用各种方法取得记录数, 各字段值,并实现first, next eof等一系列方法。 对于要实现MIDAS中返回前面字段的方式, 可以通过ado实现, 也可通过各DB原生C接口来实现, 需要使用服务端游标。
目前这种成熟应用未见有开源的。
IOCP主要是WINDOWS内核支持, 所支持连接数超多, WINDOWS网游中应用很广, 由于有了它, 网游世界中服务端的应用中WINDOWS才能和类UNIX应用二分天下。
 
另外底层服务端代码一般不建议什么delphi编写, D在服务端应用中有多种限制。网游中使用D的主要是受盛大陈大年影响用得多些。

底层通讯代码,最好使用C, 可借用代码不少, 可移植性好,可以移植到UNIX上。

不过对完成端口这种东东, LINUX中的epoll最稳定成熟,应用最大, 其它UNIX都有对应的AIO应用, 但是都不成熟和稳定。
对于WINDOWS和LINUX的这部分,要做到跨平台的兼容, 只能分别做不同的框架, 在不同底层通讯框架基础上共享应用代码。
 
多谢大家,我再问一件觉得该打的问题,不学想深入学习socket只想快点会用IOCP,是不是不可能?另外,我怕自己搞杂了,又从DELPHI转到c++上去了
 
1 不可能。
2 跟语言无关。
 
HOHO 小弟写过一段IOCP

有机会可以访问 偶的博客 huangj.com
 
谢谢,要没有情况,下周就结了?
 
谁有<<WINDOWS网络编程之DELPHI篇>>清华大学出版社这本书??或者下载地址,电子版的
 
争论的很激烈,最终还是有很多高手作了解答,作为一个新手,我也希望论坛一直都这样,才好,谢谢。
 
/*
* 服务器程序只能运行在 Windows NT,
* version 3.51 或更高的版本上.
* 不能运行在 Windows 95 上.
*/

#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <tchar.h>
#include <string.h>
#include <winsock.h>
#include <io.h>
#include <ODBCINST.H>
#include <SQLEXT.H>
#include <time.h>
#include <winbase.h>
#include &quot;MtVerify.h&quot;
#pragma comment (lib,&quot;WS2_32.lib&quot;)
#pragma comment (lib,&quot;ODBC32.lib&quot;)

#define SERV_TCP_PORT 5554
#define MAXLINE 1024

// 用 key 追踪 每一个单独的文件句柄
// 的I/O 端口的状态.
struct ContextKey
{
SOCKET sock;
char InBuffer[4]; // 输入
OVERLAPPED ovIn;
int nOutBufIndex;
char OutBuffer[MAXLINE]; // 输出
OVERLAPPED ovOut;
DWORD dwWritten;
};

struct User
{
char Subject[50];
char Name[50];
char PassWord[50];
};

/*结构体定义*/
//试卷每道题的结构
struct TestRubric{
char Questions[512];
char SelectObject[4][256];
};

//时间结构

//生成的试卷
//
// 全局变量
//

HANDLE ghCompletionPort;

//数据库全局变量
SQLHANDLE hEnv; //ODBC环境句柄
SQLHANDLE hConn; //ODBC连接句柄
SCHAR * strConnect =&quot;DRIVER={Microsoft Access Driver (*.mdb)};DBQ=&quot;;
SCHAR * DBName=&quot;exam.mdb&quot;;
SCHAR ProgPath[256];
char Conn[256]; //ODBC驱动连接返完整字符返回缓冲区
SCHAR ConnectString[1024];
char UserAttrib[50];

//
// 函数申明
//

void CreateWorkerThreads();
DWORD WINAPI ThreadFunc(LPVOID pvoid);
void IssueRead(struct ContextKey *pCntx);
void CheckOsVersion();
void FatalError(char *s);
void SendString(char *p,struct ContextKey *pCntx);

//数据库操作函数
void ODBCConnect(SCHAR *strConnect,SCHAR *ProgPath,SCHAR *DBName); //ODBC数据连接子程序
BOOL LoginSever(char * LpText,struct User *login,char *p); //登陆操作函数
BOOL GetState(struct User US,char *p); //获取试卷状态子函数
int GetNumbers(struct User US,char *P); //获取试卷状态子函数
BOOL initTestPapers(struct User US,int Num,struct TestRubric * TestPapers); //初始化试卷
void GetQuestions(struct TestRubric * TestPapers,char * buffers); //发送缓冲区格式化试题子函数;
BOOL ScanTime(struct User US,char * P,struct ContextKey *pCntx); //设置试卷状态子函数
void ChangeState(struct User US,char State[5]);
void SaveResult(struct User US,char Result[1024]);
BOOL GetResult(struct User US,char *p); //获得试卷做答子函数
void ChangeState2(struct User US,char State[5],char Result[1024]);
///////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
SOCKET listener;
SOCKET newsocket;
WSADATA WsaData;
struct sockaddr_in serverAddress;
struct sockaddr_in clientAddress;
int clientAddressLength;
int err;

printf(&quot;-----------------------------------------------------------------------/n&quot;);
printf(&quot;* 考试系统服务端程序 */n&quot;);
printf(&quot;* 服务器程序只能运行在 Windows NT,version 3.51 或更高的版本上. */n&quot;);
printf(&quot;* -------- 2003.12.25 */n&quot;);
printf(&quot;* yaomingmail@sina.com */n&quot;);
printf(&quot;-----------------------------------------------------------------------/n&quot;);
GetCurrentDirectory(256,ProgPath); //获得程序路径
ProgPath[strlen(ProgPath)]='//';
ODBCConnect(strConnect,ProgPath,DBName); //建立数据库联接

CheckOsVersion();

err = WSAStartup (0x0101, &amp;WsaData);
if (err == SOCKET_ERROR)
{
FatalError(&quot;网络初始化失败.&quot;);
return EXIT_FAILURE;
}

listener = socket(AF_INET, SOCK_STREAM, 0); //开启一个socket 套节字
if (listener < 0)
{
FatalError(&quot;socket() 错误-请检查 TCP/IP 是否正确安装?&quot;);
return EXIT_FAILURE;
}

memset(&amp;serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(SERV_TCP_PORT);

err = bind(listener, // 绑定我们的局域地址
(struct sockaddr *)&amp;serverAddress,
sizeof(serverAddress));
if (err < 0)
FatalError(&quot;bind() 错误-请检查 TCP/IP 是否正确安装?&quot;);

ghCompletionPort = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
NULL,0,0);
if (ghCompletionPort == NULL)
FatalError(&quot;CreateIoCompletionPort() 错误-请检查系统是否是Windows NT version 3.51 或更高版本.&quot;);

CreateWorkerThreads(ghCompletionPort);

listen(listener, 5);

fprintf(stderr, &quot;服务器启用 I/O Completion Ports 模式:端口 %d/n&quot;, SERV_TCP_PORT);
fprintf(stderr, &quot;/nCtrl+C 停止服务器程序/n&quot;);
printf(&quot;/n-----------------------------------------------------------------------/n&quot;);
fprintf(stderr, &quot;开始监听客户端:/n&quot;);

//
// 无限循环,接受并处理新的连接
//
for (;;)
{
struct ContextKey *pKey;

clientAddressLength = sizeof(clientAddress);
newsocket = accept(listener,
(struct sockaddr *)&amp;clientAddress,
&amp;clientAddressLength);
if (newsocket < 0)
{
FatalError(&quot;accept() 错误.&quot;);
return EXIT_FAILURE;
}
/*
*建立一个 key 并初始化它.
*/
pKey = calloc(1, sizeof(struct ContextKey)); // calloc 将使 buffer 区域清零.
pKey->sock = newsocket;
pKey->ovOut.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//为写信息包过程设置(事件对象) event .
pKey->ovOut.hEvent = (HANDLE)((DWORD)pKey->ovOut.hEvent | 0x1);

CreateIoCompletionPort( //为请求绑定端口
(HANDLE)newsocket,
ghCompletionPort,
(DWORD)pKey,
0
);
IssueRead(pKey); // 完成第一次读操作
}
SQLDisconnect(hConn);
SQLFreeHandle(SQL_HANDLE_DBC,hConn);
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
return 0;
}

void CreateWorkerThreads()
{
SYSTEM_INFO sysinfo;
DWORD dwThreadId;
DWORD dwThreads;
DWORD i;

GetSystemInfo(&amp;sysinfo);
dwThreads = sysinfo.dwNumberOfProcessors * 2 + 2;
for (i=0; i<dwThreads; i++)
{
HANDLE hThread;
hThread = CreateThread(
NULL, 0, ThreadFunc, NULL, 0, &amp;dwThreadId
);
CloseHandle(hThread);
}
}

//
// 每一个工作线程从这里开始.
//
DWORD WINAPI ThreadFunc(LPVOID pVoid)
{
struct User users;
struct TestRubric * TestPapers;
BOOL bResult;
DWORD dwNumRead;
struct ContextKey *pCntx;
LPOVERLAPPED lpOverlapped;
char Temp[50];
int i,QueNums;
char buffers[1024];

UNREFERENCED_PARAMETER(pVoid);

// 无限循环从 I/O completion port 获取信息.
for (;;)
{
bResult = GetQueuedCompletionStatus(
ghCompletionPort,
&amp;dwNumRead,
&amp;(DWORD)pCntx,
&amp;lpOverlapped,
INFINITE
);

if (bResult == FALSE
&amp;&amp; lpOverlapped == NULL)
{
FatalError(
&quot;ThreadFunc - GetQueuedCompletionStatus()错误./n&quot;);
}

else if (bResult == FALSE
&amp;&amp; lpOverlapped != NULL)
{
// This happens occasionally instead of
// end-of-file. Not sure why.
closesocket(pCntx->sock);
free(pCntx);
fprintf(stderr,&quot;用户非正常退出./n&quot;);
}

else if (dwNumRead == 0)
{
closesocket(pCntx->sock);
free(pCntx);
fprintf(stderr, &quot;用户已经关闭端口./n&quot;);
fprintf(stderr, &quot;------------------./n&quot;);
}

else
{
char *pch = &amp;pCntx->OutBuffer[pCntx->nOutBufIndex++];
*pch++ = pCntx->InBuffer[0];
*pch = '/0';
if (pCntx->InBuffer[0] == '/n')
{
for(i=0;i<6;i++)
Temp=pCntx->OutBuffer;
Temp='/0';
if(!strcmp(Temp,&quot;login:&quot;))
{
if(LoginSever(pCntx->OutBuffer,&amp;users,Temp))
SendString(Temp,pCntx);
else
SendString(&quot;无法识别的用户.&quot;,pCntx);
}
else if(!strcmp(Temp,&quot;Srecv:&quot;))
{
for(i=0;i<7;i++)
Temp=pCntx->OutBuffer[i+6];
Temp='/0';
if (!strcmp(Temp,&quot;ScanTm:&quot;))
if(ScanTime(users,buffers,pCntx))
fprintf(stderr,&quot;%s %s试卷状态设置成功./n&quot;,users.Name,users.Subject);
else
{
fprintf(stderr,&quot;设置 %s 的 %s 试卷状态失败./n&quot;,users.Name,users.Subject);
SendString(&quot;服务器设置试卷状态失败.&quot;,pCntx);
}
else if(!strcmp(Temp,&quot;GetSta:&quot;))
{
if(GetState(users,Temp))
SendString(Temp,pCntx);
}
else if(!strcmp(Temp,&quot;GetRlt:&quot;))
{
if (GetResult(users,buffers))
SendString(buffers,pCntx);
else SendString(&quot;Erro&quot;,pCntx);
}
else if(!strcmp(Temp,&quot;GetNum:&quot;))
{
if(QueNums=GetNumbers(users,buffers))
{
SendString(buffers,pCntx);
fprintf(stderr,&quot;用户获取 %s 试卷总题数 %d 成功./n&quot;,users.Subject,QueNums);
TestPapers=(struct TestRubric *)malloc(sizeof(struct TestRubric)*QueNums);
if(TestPapers==NULL)
fprintf(stderr,&quot;申请动态内存失败.&quot;);
else if(!initTestPapers(users,QueNums,TestPapers))
{
fprintf(stderr,&quot;初始化试卷失败./n&quot;);
free(TestPapers);
}
else fprintf(stderr,&quot;初始化试卷成功./n&quot;);
}
else fprintf(stderr,&quot;用户获取 %s 试卷总题数失败./n&quot;,users.Subject);
}
else if(!strcmp(Temp,&quot;GetQue:&quot;))
{
for(i=0;pCntx->OutBuffer!='/n';i++);
pCntx->OutBuffer='/0';
i=0;
i=atoi(&amp;pCntx->OutBuffer[13]);
GetQuestions(TestPapers+i,buffers);
SendString(buffers,pCntx);
fprintf(stderr,&quot;成功发送 %s : %s 试卷的第 %d 题./n&quot;,users.Name,users.Subject,i+1);
}
else if(!strcmp(Temp,&quot;SaveDt:&quot;))
{
SaveResult(users,&amp;(pCntx->OutBuffer[13]));
}
else if(!strcmp(Temp,&quot;ChanST:&quot;))
ChangeState2(users,&quot;3&quot;,&amp;(pCntx->OutBuffer[13]));
}
else if(!strcmp(Temp,&quot;trecv:&quot;)){}
else if(!strcmp(Temp,&quot;arecv:&quot;)){}
else if(!strcmp(Temp,&quot;ssave:&quot;)){}
else if(!strcmp(Temp,&quot;tsave:&quot;)){}
else if(!strcmp(Temp,&quot;asave:&quot;)){}

pCntx->nOutBufIndex = 0;
fprintf(stderr, &quot; Echo on socket %x./n&quot;, pCntx->sock);
}

// 开始读取
IssueRead(pCntx);
}
}

return 0;
}

/*
* 调用 ReadFile 开始一个异步请求
* 重复获取我们的句柄状态
*/
void IssueRead(struct ContextKey *pCntx)
{
int i = 0;
BOOL bResult;
int err;
int numRead;

while (++i)
{
// 读取一个单字符
bResult = ReadFile(
(HANDLE)pCntx->sock,
pCntx->InBuffer,
1,
&amp;numRead,
&amp;pCntx->ovIn
);

// 这里,等待一个信息包.
if (bResult)
return;

err = GetLastError();

// 获取状态,这里并不代表错误
if (err == ERROR_IO_PENDING)
return;

if ( err == ERROR_INVALID_USER_BUFFER ||
err == ERROR_NOT_ENOUGH_QUOTA ||
err == ERROR_NOT_ENOUGH_MEMORY )
{
if (i == 5)
{
Sleep(50); // 等待并重试
continue;
}

FatalError(&quot;IssueRead -系统溢出连接池&quot;);
}

break;
}

fprintf(stderr, &quot;IssueRead - 读信息失败./n&quot;);
}

//
// 确保我们运行在正确的版本下 Windows NT (3.51, 4.0, or later)
//
void CheckOsVersion()
{
OSVERSIONINFO ver;
BOOL bResult;

ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

bResult = GetVersionEx((LPOSVERSIONINFO) &amp;ver);

if ( (!bResult) ||
(ver.dwPlatformId != VER_PLATFORM_WIN32_NT) )
{
FatalError(&quot;ECHOSRV requires Windows NT 3.51 or later.&quot;);
}

}

//发送字符串子程序
void SendString(char *p,struct ContextKey *pCntx)
{
int len;
strcpy(pCntx->OutBuffer,p);
len=strlen(pCntx->OutBuffer);
pCntx->OutBuffer[len++]='/n';
pCntx->OutBuffer[len]='/0';
pCntx->nOutBufIndex=strlen(pCntx->OutBuffer);
WriteFile(
(HANDLE)(pCntx->sock),
pCntx->OutBuffer,
pCntx->nOutBufIndex,
&amp;pCntx->dwWritten,
&amp;pCntx->ovOut
);

}

//
// 错误句柄
//
void FatalError(char *s)
{
fprintf(stdout, &quot;%s/n&quot;, s);
exit(EXIT_FAILURE);
}

//数据库联接函数
void ODBCConnect(SCHAR *strConnect,SCHAR *ProgPath,SCHAR *DBName)
{
SWORD StrLen; //返回缓冲区长度
SQLRETURN RB; //ODBC函数返回错误校验变量
strcat(ConnectString,strConnect);
strcat(ConnectString,ProgPath);
strcat(ConnectString,DBName);

RB=SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE,&amp;hEnv); //获取环境句柄
if (RB==SQL_SUCCESS || RB==SQL_SUCCESS_WITH_INFO)
{
RB=SQLSetEnvAttr(hEnv,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,0); //设置OBDC启用版本
if (RB==SQL_SUCCESS || RB==SQL_SUCCESS_WITH_INFO)
{
RB=SQLAllocHandle(SQL_HANDLE_DBC,hEnv,&amp;hConn); //获取连接句柄
if (RB==SQL_SUCCESS || RB==SQL_SUCCESS_WITH_INFO)
{
RB=SQLDriverConnect(hConn,NULL,(LPSTR)ConnectString,(SQLSMALLINT)strlen(ConnectString),(LPSTR)Conn,(SQLSMALLINT)strlen(Conn),&amp;StrLen,SQL_DRIVER_NOPROMPT); //联接OBDC驱动程序
if (RB==SQL_SUCCESS || RB==SQL_SUCCESS_WITH_INFO)
printf(&quot;/n学生考试系统数据库连接成功:/n%s%s/n/n&quot;,ProgPath,DBName);
else
{
SQLFreeHandle(SQL_HANDLE_DBC, hConn); //释放句柄
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
FatalError(&quot;ODBC驱动程序连接失败!/n&quot;);
}
}
else
{
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
FatalError(&quot;ODBC连接句柄获取失败!/n&quot;);
}
}
else
{
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
FatalError(&quot;设置ODBC工作版本失败!/n&quot;);
}
}
else
FatalError(&quot;ODBC环境句柄获取失败!/n&quot;);
}

//登陆操作函数
BOOL LoginSever(char * LpText,struct User *login,char *p)
{
SQLINTEGER PassLength; //返回缓冲区长度
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量
char * Temp;
char Attribute[50];
char *SQLStatement=&quot;select 用户等级 from 用户管理 where 科目=? and 账号=? and 密码=?&quot;;

LpText+=6;
Temp=LpText;
while(*Temp)
{
if(*Temp=='#')
*Temp='/0';
Temp++;
}
strcpy(login->Subject,LpText);
LpText+=(strlen(LpText)+1);
strcpy(login->Name,LpText);
LpText+=(strlen(LpText)+1);
strcpy(login->PassWord,LpText);

fprintf(stderr, &quot;/n*****************************/n&quot;);
fprintf(stderr, &quot;用户:%s............请求登陆./n&quot;,login->Name);
fprintf(stderr, &quot;科目:%s /n&quot;,login->Subject);
fprintf(stderr, &quot;密码/考号:%s/n&quot;,login->PassWord);

RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,login->Subject,strlen(login->Subject),NULL);
SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,login->Name,strlen(login->Name),NULL);
SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,login->PassWord,strlen(login->PassWord),NULL);
SQLPrepare(hStmt,SQLStatement,strlen(SQLStatement));
RC=SQLExecute(hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindCol(hStmt,1,SQL_C_CHAR,Attribute,strlen(Attribute),&amp;PassLength);
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;用户登陆失败./n&quot;);
fprintf(stderr, &quot;*****************************/n/n&quot;);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 0;
}
fprintf(stderr, &quot;用户登陆成功./n&quot;);
fprintf(stderr, &quot;用户属于/&quot;%s/&quot;./n&quot;,Attribute);
fprintf(stderr, &quot;*****************************/n/n&quot;);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
strcpy(p,Attribute);
return 1;
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
return 0;
}
}
else
{
fprintf(stderr,&quot;/n语句句柄获取失败/n&quot;);
return 0;
}
}
//获取试卷状态子程序
BOOL GetState(struct User US,char *p)
{
char *SQLStatement=&quot;select 试卷状态 from 试卷信息表 where 科目名称=?&quot;;
char *SQLStatement2=&quot;select 试卷状态 from 学生试卷成绩表 where 科目=? and 姓名=? and 考号=?&quot;;
SQLINTEGER PassLength; //返回缓冲区长度
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量
char State[5];
char State2[5];

RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLExecDirect(hStmt,SQLStatement,strlen(SQLStatement));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindCol(hStmt,1,SQL_C_CHAR,State,strlen(State),&amp;PassLength);
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;没有用户所要的 %s 试卷./n&quot;,US.Subject);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
strcpy(p,&quot;0&quot;);
return 1;
}
if(!strcmp(State,&quot;0&quot;))
{
fprintf(stderr, &quot;用户所要试卷还未经过审核./n&quot;);
strcpy(p,State);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 1;
}
SQLFreeStmt(hStmt,SQL_RESET_PARAMS);
SQLFreeStmt(hStmt,SQL_UNBIND);
SQLFreeStmt(hStmt,SQL_CLOSE);
RC=SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Name,strlen(US.Name),NULL);
RC=SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.PassWord,strlen(US.PassWord),NULL);
RC=SQLExecDirect(hStmt,SQLStatement2,strlen(SQLStatement2));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindCol(hStmt,1,SQL_C_CHAR,State2,strlen(State2),&amp;PassLength);
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;无此用户的试卷信息./n&quot;);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 0;
}
strcpy(p,State2);
fprintf(stderr, &quot;用户获取试卷状态成功./n&quot;);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 1;
}
}
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
return 0;

}
else
{
fprintf(stderr,&quot;/n语句句柄获取失败/n&quot;);
return 0;
}
}

//获取试卷信息子函数
int GetNumbers(struct User US,char *P)
{
char *SQLStatement=&quot;select 建考时间,开考时间,结束时间,监考人,题目数量 from 试卷信息表 where 科目名称=?&quot;;
int Num=0;
SQLINTEGER sCustID,sCustID1,sCustID2,sCustID3,sCustID4; //返回缓冲区长度
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量
DATE_STRUCT Date;
TIMESTAMP_STRUCT Time1,Time2;
char People[20];

RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLExecDirect(hStmt,SQLStatement,strlen(SQLStatement));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindCol(hStmt,1,SQL_C_DATE,&amp;Date,0,&amp;sCustID);
SQLBindCol(hStmt,2,SQL_C_TIMESTAMP,&amp;Time1,0,&amp;sCustID1);
SQLBindCol(hStmt,3,SQL_C_TIMESTAMP,&amp;Time2,0,&amp;sCustID2);
SQLBindCol(hStmt,4,SQL_C_CHAR,&amp;People,20,&amp;sCustID3);
SQLBindCol(hStmt,5,SQL_C_USHORT,&amp;Num,0,&amp;sCustID4);
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;没有用户所要的 %s 试卷./n&quot;,US.Subject);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 0;
}
wsprintf(P,&quot;%d#%d#%d#%d#%d#%d#%d#%d#%d#%d#%s&quot;,Date.year,Date.month,Date.day,Time1.hour,Time1.minute,Time1.second,Time2.hour,Time2.minute,Time2.second,Num,People);
return Num;
}
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
return 0;
}
fprintf(stderr,&quot;/n语句句柄获取失败/n&quot;);
return 0;
}

//格式化试题子函数
void GetQuestions(struct TestRubric * TestPapers,char * buffers)
{
int i,n;
strcpy(buffers,TestPapers->Questions);
n=strlen(buffers);
buffers[n++]='#';
for(i=0;i<4;i++)
{
strcpy(&amp;buffers[n],TestPapers->SelectObject);
n=strlen(buffers);
buffers[n++]='#';
}
buffers[n]='/0';
}

//初始化试卷
BOOL initTestPapers(struct User US,int Num,struct TestRubric * TestPapers)
{
char *SQLStatement=&quot;select 考题序列 from 学生试卷成绩表 where 科目=? and 姓名=? and 考号=?&quot;;
char *SQLStatement2=&quot;select * from 题库表 where 题号=?&quot;;
SQLINTEGER PassLength,TestID,TestID2,TestID3,TestID4,TestID5; //返回缓冲区长度
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量
char TestPaper[512];
char Temp[28];
int i,j,n,k=0;
SQLINTEGER Test;
struct TestRubric BinTest;

int *THao=malloc(sizeof(int)*Num);
RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
RC=SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Name,strlen(US.Name),NULL);
RC=SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.PassWord,strlen(US.PassWord),NULL);
RC=SQLExecDirect(hStmt,SQLStatement,strlen(SQLStatement));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindCol(hStmt,1,SQL_C_CHAR,TestPaper,strlen(TestPaper),&amp;PassLength);
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;没有用户所要的 %s 试卷./n&quot;,US.Subject);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 0;
}
n=strlen(TestPaper);
for(i=0;i<n;i++)
{
for (j=0;TestPaper!='#';j++,i++)
Temp[j]=TestPaper;
Temp[j]='/0';
THao[k++]=atoi(Temp);
}
SQLFreeStmt(hStmt,SQL_UNBIND);
SQLFreeStmt(hStmt,SQL_CLOSE);
RC=SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_ULONG,SQL_INTEGER,10,0,&amp;Test,0,NULL);
SQLBindCol(hStmt,4,SQL_C_CHAR,BinTest.Questions,strlen(BinTest.Questions),&amp;TestID);
SQLBindCol(hStmt,5,SQL_C_CHAR,BinTest.SelectObject[0],strlen(BinTest.SelectObject[0]),&amp;TestID2);
SQLBindCol(hStmt,6,SQL_C_CHAR,BinTest.SelectObject[1],strlen(BinTest.SelectObject[1]),&amp;TestID3);
SQLBindCol(hStmt,7,SQL_C_CHAR,BinTest.SelectObject[2],strlen(BinTest.SelectObject[2]),&amp;TestID4);
SQLBindCol(hStmt,8,SQL_C_CHAR,BinTest.SelectObject[3],strlen(BinTest.SelectObject[3]),&amp;TestID5);
for(i=0;i<Num;i++)
{
Test=THao;
SQLPrepare(hStmt,SQLStatement2,strlen(SQLStatement2));
RC=SQLExecute(hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;没有找到用户所要 %s 试卷的第 %d 题./n&quot;,US.Subject,i);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
free(THao);
return 0;
}
strcpy(TestPapers->Questions,BinTest.Questions);
strcpy(TestPapers->SelectObject[0],BinTest.SelectObject[0]);
strcpy(TestPapers->SelectObject[1],BinTest.SelectObject[1]);
strcpy(TestPapers->SelectObject[2],BinTest.SelectObject[2]);
strcpy(TestPapers->SelectObject[3],BinTest.SelectObject[3]);
TestPapers++;
SQLFreeStmt(hStmt,SQL_CLOSE);
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
free(THao);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
free(THao);
return 0;
}
}
free(THao);
return 1;
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
free(THao);
fprintf(stderr,&quot;获取题目集合失败./n&quot;);
return 0;
}
}
else
{
free(THao);
fprintf(stderr,&quot;获取语句句柄失败./n&quot;);
return 0;
}
}

BOOL ScanTime(struct User US,char * P,struct ContextKey *pCntx)
{
char *SQLStatement=&quot;select 建考时间,开考时间,结束时间 from 试卷信息表 where 科目名称=?&quot;;
char *SQLStatement2=&quot;select 试卷状态 from 学生试卷成绩表 where 科目=? and 姓名=? and 考号=?&quot;;
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量
DATE_STRUCT Date;
SQLINTEGER sCustID,sCustID1,sCustID2,PassLength;
TIMESTAMP_STRUCT Time1,Time2;
SYSTEMTIME TM;
char State[5];

RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
RC=SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Name,strlen(US.Name),NULL);
RC=SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.PassWord,strlen(US.PassWord),NULL);
RC=SQLExecDirect(hStmt,SQLStatement2,strlen(SQLStatement2));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindCol(hStmt,1,SQL_C_CHAR,State,strlen(State),&amp;PassLength);
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;无此用户的试卷信息./n&quot;);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 0;
}
fprintf(stderr, &quot;用户获取试卷状态成功./n&quot;);
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
return 0;
}
SQLFreeStmt(hStmt,SQL_RESET_PARAMS);
SQLFreeStmt(hStmt,SQL_UNBIND);
SQLFreeStmt(hStmt,SQL_CLOSE);
SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLExecDirect(hStmt,SQLStatement,strlen(SQLStatement));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindCol(hStmt,1,SQL_C_DATE,&amp;Date,0,&amp;sCustID);
SQLBindCol(hStmt,2,SQL_C_TIMESTAMP,&amp;Time1,0,&amp;sCustID1);
SQLBindCol(hStmt,3,SQL_C_TIMESTAMP,&amp;Time2,0,&amp;sCustID2);
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;没有用户所要的 %s 试卷./n&quot;,US.Subject);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 0;
}
GetSystemTime(&amp;TM);
TM.wHour+=8;
if(TM.wYear<Date.year||
(TM.wYear==Date.year&amp;&amp;TM.wMonth<Date.month)||
(TM.wYear==Date.year&amp;&amp;TM.wMonth==Date.month&amp;&amp;TM.wDay<Date.day)||
(TM.wYear==Date.year&amp;&amp;TM.wMonth==Date.month&amp;&amp;TM.wDay==Date.day&amp;&amp;TM.wHour<Time1.hour)||
(TM.wYear==Date.year&amp;&amp;TM.wMonth==Date.month&amp;&amp;TM.wDay==Date.day&amp;&amp;TM.wHour==Time1.hour&amp;&amp;TM.wMinute<Time1.minute)||
(TM.wYear==Date.year&amp;&amp;TM.wMonth==Date.month&amp;&amp;TM.wDay==Date.day&amp;&amp;TM.wHour==Time1.hour&amp;&amp;TM.wMinute==Time1.minute&amp;&amp;TM.wSecond<Time1.second))
//将试卷状态改为0
{
ChangeState(US,&quot;2&quot;);
SendString(&quot;服务器设置试卷状态成功.&quot;,pCntx);
}
else if(TM.wYear>Date.year||
(TM.wYear==Date.year&amp;&amp;TM.wMonth>Date.month)||
(TM.wYear==Date.year&amp;&amp;TM.wMonth==Date.month&amp;&amp;TM.wDay>Date.day)||
(TM.wYear==Date.year&amp;&amp;TM.wMonth==Date.month&amp;&amp;TM.wDay==Date.day&amp;&amp;TM.wHour>Time2.hour)||
(TM.wYear==Date.year&amp;&amp;TM.wMonth==Date.month&amp;&amp;TM.wDay==Date.day&amp;&amp;TM.wHour==Time2.hour&amp;&amp;TM.wMinute>Time2.minute)||
(TM.wYear==Date.year&amp;&amp;TM.wMonth==Date.month&amp;&amp;TM.wDay==Date.day&amp;&amp;TM.wHour==Time2.hour&amp;&amp;TM.wMinute==Time2.minute&amp;&amp;TM.wSecond>Time2.second))
//将试卷状态改为3
{
ChangeState(US,&quot;3&quot;);
SendString(&quot;服务器设置试卷状态成功.&quot;,pCntx);
}
else
//将试卷状态改为1
{
if(strcmp(State,&quot;3&quot;))
ChangeState(US,&quot;1&quot;);
sprintf(P,&quot;%c%u&quot;,'t',(Time2.hour-TM.wHour)*60*60+(Time2.minute-TM.wMinute)*60+(Time2.second-TM.wSecond));
SendString(P,pCntx);
}
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 1;
}
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
return 0;
}
fprintf(stderr,&quot;/n语句句柄获取失败/n&quot;);
return 0;
}

//改变试卷状态子函数
void ChangeState(struct User US,char State[5])
{
char *SQLStatement2=&quot;update 学生试卷成绩表 set 试卷状态=? where 科目=? and 姓名=? and 考号=?&quot;;
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量

RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
RC=SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,5,0,State,strlen(State),NULL);
RC=SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Name,strlen(US.Name),NULL);
RC=SQLBindParameter(hStmt,4,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.PassWord,strlen(US.PassWord),NULL);
RC=SQLExecDirect(hStmt,SQLStatement2,strlen(SQLStatement2));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
fprintf(stderr, &quot;设置试卷状态成功./n&quot;);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
}
}
else
fprintf(stderr,&quot;/n语句句柄获取失败/n&quot;);
}

void SaveResult(struct User US,char Result[1024])
{
char *SQLStatement2=&quot;update 学生试卷成绩表 set 答案序列=? where 科目=? and 姓名=? and 考号=?&quot;;
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量
char *Temp;
int LpText;

Temp=Result;
while(*Temp!='*')
{
if(*Temp=='#')
*Temp='/0';
Temp++;
}
*Temp='/0';
strcpy(US.Name,Result);
LpText=strlen(Result)+1;
strcpy(US.Subject,&amp;Result[LpText]);
LpText+=(strlen(&amp;Result[LpText])+1);
strcpy(US.PassWord,&amp;Result[LpText]);
LpText+=(strlen(&amp;Result[LpText])+1);
Result[LpText+strlen(&amp;Result[LpText])-1]='/0';

RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
RC=SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_LONGVARCHAR,1024,0,&amp;Result[LpText],strlen(&amp;Result[LpText]),NULL);
RC=SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Name,strlen(US.Name),NULL);
RC=SQLBindParameter(hStmt,4,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.PassWord,strlen(US.PassWord),NULL);
RC=SQLExecDirect(hStmt,SQLStatement2,strlen(SQLStatement2));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
fprintf(stderr, &quot;%s 保存%s试卷成功./n&quot;,US.Name,US.Subject);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
}
}
else
fprintf(stderr,&quot;/n语句句柄获取失败/n&quot;);

}

BOOL GetResult(struct User US,char *p)
{
char *SQLStatement=&quot;select 答案序列 from 学生试卷成绩表 where 科目=? and 姓名=? and 考号=?&quot;;
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量
SQLINTEGER sCustID;
char buffers[1024];

RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
RC=SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Name,strlen(US.Name),NULL);
RC=SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.PassWord,strlen(US.PassWord),NULL);
RC=SQLExecDirect(hStmt,SQLStatement,strlen(SQLStatement));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
SQLBindCol(hStmt,1,SQL_C_CHAR,buffers,strlen(buffers),&amp;sCustID);
if(SQLFetch(hStmt)==SQL_NO_DATA)
{
fprintf(stderr, &quot;无此用户的试卷作答信息./n&quot;);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
return 0;
}
if(buffers[0]<'0'||buffers[0]>'4') return 0;
strcpy(p,buffers);
fprintf(stderr, &quot;用户获取试卷作答信息成功./n&quot;);
return 1;
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
return 0;
}

}
fprintf(stderr,&quot;/n语句句柄获取失败/n&quot;);
return 0;
}

void ChangeState2(struct User US,char State[5],char Result[1024])
{
char *SQLStatement2=&quot;update 学生试卷成绩表 set 试卷状态=? where 科目=? and 姓名=? and 考号=?&quot;;
SQLHANDLE hStmt; //ODBC语句句柄
SQLRETURN RC; //ODBC函数返回错误校验变量
char *Temp;
int LpText;

Temp=Result;
while(*Temp!='*')
{
if(*Temp=='#')
*Temp='/0';
Temp++;
}
*Temp='/0';
strcpy(US.Name,Result);
LpText=strlen(Result)+1;
strcpy(US.Subject,&amp;Result[LpText]);
LpText+=(strlen(&amp;Result[LpText])+1);
strcpy(US.PassWord,&amp;Result[LpText]);
LpText+=(strlen(&amp;Result[LpText])+1);
Result[LpText+strlen(&amp;Result[LpText])-1]='/0';

RC=SQLAllocHandle(SQL_HANDLE_STMT, hConn, &amp;hStmt);
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
RC=SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,5,0,State,strlen(State),NULL);
RC=SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Subject,strlen(US.Subject),NULL);
RC=SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.Name,strlen(US.Name),NULL);
RC=SQLBindParameter(hStmt,4,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,50,0,US.PassWord,strlen(US.PassWord),NULL);
RC=SQLExecDirect(hStmt,SQLStatement2,strlen(SQLStatement2));
if (RC==SQL_SUCCESS || RC==SQL_SUCCESS_WITH_INFO)
{
fprintf(stderr, &quot;设置试卷状态成功./n&quot;);
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
fprintf(stderr,&quot;/nSQL语句执行失败/n&quot;);
}
}
else
fprintf(stderr,&quot;/n语句句柄获取失败/n&quot;);
}
 
谁有<<WINDOWS网络编程之DELPHI篇>>清华大学出版社这本书??或者下载地址,电子版的
 
10月23日
一个完成端口的例子
完成端口是基于Windows 重叠IO 的 一种IO 管理模型,提供了最高的效率. 下面是练习使用的一个例子.服务器为别人所写.我只是消化了一下.为了配合测试,写了一个小小的客户端. 都放在这里,加深自己的记忆把.
服务器代码:
// IOCP.cpp : 定义控制台应用程序的入口点。
//
#include &quot;stdafx.h&quot;
#define _WIN32_WINNT 0x0500
#include <cstdlib>
#include <clocale>
#include <ctime>
#include <iostream>
#include <vector>
#include <algorithm>
#include <WinSock2.h>
#include <mswsock.h>
using namespace std;
#pragma comment(lib, &quot;ws2_32.lib&quot;)
#pragma comment(lib, &quot;mswsock.lib&quot;)
const int MAX_BUFFER_SIZE = 1024;
const int PRE_SEND_SIZE = 1024;
const int QUIT_TIME_OUT = 3000; //退出时主线程分80次Sleep完3000MS, 以等待workThread退出
const int PRE_DOT_TIMER = QUIT_TIME_OUT / 80;
typedef enum{IoTransFile, IoSend, IoRecv, IoQuit} IO_TYPE;
typedef struct
{
SOCKET hSocket;
SOCKADDR_IN ClientAddr;
} PRE_SOCKET_DATA, *PPRE_SOCKET_DATA;
typedef struct
{
OVERLAPPED oa;
WSABUF DataBuf;
char Buffer[MAX_BUFFER_SIZE];
IO_TYPE IoType;
} PRE_IO_DATA, *PPRE_IO_DATA;
typedef vector<PPRE_SOCKET_DATA> SocketDataVector;
typedef vector<PPRE_IO_DATA> IoDataVector;
SocketDataVector gSockDataVec;
IoDataVector gIoDataVec;
CRITICAL_SECTION csProtection;
char *TimeNow(void)
{
time_t t = time(NULL);
tm *localtm = localtime(&amp;t);
static char timemsg[512]={0};
strftime(timemsg, 512, &quot;%Z: %B %d %X %Y&quot;, localtm);
return timemsg;
}
BOOL TransFile(PPRE_IO_DATA pIoData, PPRE_SOCKET_DATA pSocketData, DWORD dwNameLen)
{
pIoData->Buffer[dwNameLen-1] = '/0';
HANDLE hFile = CreateFile(pIoData->Buffer, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
BOOL bRet = FALSE;
if (hFile != INVALID_HANDLE_VALUE)
{
cout<<&quot;Transmit File&quot;<<pIoData->Buffer<<&quot;to client&quot;<<endl;
pIoData->IoType = IoTransFile;
memset(&amp;pIoData->oa, 0, sizeof(OVERLAPPED));
*reinterpret_cast<HANDLE*>(pIoData->Buffer) = hFile; // 此处保存句柄是为了在传输完毕后关闭文件
TransmitFile(pSocketData->hSocket, hFile, GetFileSize(hFile, NULL),
PRE_SEND_SIZE, reinterpret_cast<LPOVERLAPPED>(pIoData),
NULL, TF_USE_SYSTEM_THREAD);
bRet =(WSAGetLastError() == WSA_IO_PENDING);
}
else
cout<<&quot;Transmit File&quot;<<&quot;Error:&quot;<<GetLastError()<<endl;
return bRet;
}
DWORD WINAPI ThreadProc(LPVOID IocpHandle)
{
DWORD dwRecv = 0;
DWORD dwFlags = 0;
HANDLE hIocp = reinterpret_cast<HANDLE> (IocpHandle);
DWORD dwTransCount = 0;
PPRE_IO_DATA pPreIoData = NULL;
PPRE_SOCKET_DATA pPreHandleData = NULL;

while (TRUE)
{
if (GetQueuedCompletionStatus(hIocp, &amp;dwTransCount,
reinterpret_cast<LPDWORD>(&amp;pPreHandleData),
reinterpret_cast<LPOVERLAPPED*>(&amp;pPreIoData), INFINITE))
{
if (0 == dwTransCount &amp;&amp; IoQuit != pPreIoData->IoType)
{
cout<<&quot;Client&quot;<<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
<<&quot;:&quot;<<ntohs(pPreHandleData->ClientAddr.sin_port)
<<&quot; is closed&quot;<<endl;
closesocket(pPreHandleData->hSocket);
EnterCriticalSection(&amp;csProtection);
IoDataVector::iterator itIoDelete = find(gIoDataVec.begin(),
gIoDataVec.end(), pPreIoData);
gIoDataVec.erase(itIoDelete);

// 这个地方用vector不合理,删除成本太高,不如改用list
SocketDataVector::iterator itSockDel = find(gSockDataVec.begin(),
gSockDataVec.end(), pPreHandleData);
gSockDataVec.erase(itSockDel);
LeaveCriticalSection(&amp;csProtection);
delete *itIoDelete;
delete *itSockDel;
continue;
}
switch(pPreIoData->IoType)
{
case IoTransFile: // 传送文件完成
cout<<&quot;Client&quot;<<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
<<&quot;:&quot;<<ntohs(pPreHandleData->ClientAddr.sin_port)
<<&quot;Transmit finished&quot;<<endl;
CloseHandle(reinterpret_cast<HANDLE*>(pPreIoData->Buffer));
goto LRERECV;
case IoSend:
cout<<&quot;Client&quot;<<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
<<&quot;:&quot;<<ntohs(pPreHandleData->ClientAddr.sin_port)
<<&quot;send finished&quot;<<endl;
// 处理结束后,重新发起WSARecv请求:
LRERECV:
pPreIoData->IoType = IoRecv;
pPreIoData->DataBuf.len = MAX_BUFFER_SIZE;
memset(&amp;pPreIoData->oa, 0, sizeof(OVERLAPPED));
WSARecv(pPreHandleData->hSocket, &amp;pPreIoData->DataBuf,
1, &amp;dwRecv, &amp;dwFlags,
reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData), NULL);
break;
case IoRecv:
cout<<&quot;Client:&quot;<<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)
<<&quot;:&quot;<<ntohs(pPreHandleData->ClientAddr.sin_port)
<<&quot;recv finished&quot;<<endl;
pPreIoData->IoType = IoSend;
if (!TransFile(pPreIoData, pPreHandleData, dwTransCount))
{
memset(&amp;pPreIoData->oa,0, sizeof(OVERLAPPED));
strcpy(pPreIoData->DataBuf.buf, &quot;File transmit error!/r/n&quot;);
pPreIoData->DataBuf.len = strlen(pPreIoData->DataBuf.buf);
WSASend(pPreHandleData->hSocket, &amp;pPreIoData->DataBuf, 1,
&amp;dwRecv, dwFlags, reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData), NULL);
}
break;
case IoQuit:
goto LQUIT;
default:
;
}
}
}
LQUIT:
return 0;
}
HANDLE hIocp = NULL;
SOCKET hListen = NULL;
BOOL WINAPI ShutdownHandler(DWORD dwCtrlType)
{
PRE_SOCKET_DATA PreSockData = {0};
PRE_IO_DATA PreIoData = {0};
PreIoData.IoType = IoQuit;
if (hIocp)
{
PostQueuedCompletionStatus(hIocp, 1,
reinterpret_cast<ULONG_PTR>(&amp;PreSockData),
reinterpret_cast<LPOVERLAPPED>(&amp;PreIoData));
cout<<&quot;Shutdown at&quot;<<TimeNow()<<endl<<&quot;Wait for a moment please&quot;<<endl;
for (int t = 0; t < 80; t++)
{
Sleep(PRE_DOT_TIMER);
cout<<&quot;.&quot;;
}
CloseHandle(hIocp);
}
int i = 0;
for (; i < gSockDataVec.size(); i++)
{
PPRE_SOCKET_DATA pSockData = gSockDataVec;
closesocket(pSockData->hSocket);
delete pSockData;
}
for (i= 0; i <gIoDataVec.size(); i++)
{
PPRE_IO_DATA pIoData = gIoDataVec;
delete pIoData;
}
DeleteCriticalSection(&amp;csProtection);
if (hListen)
closesocket(hListen);
WSACleanup();
exit(0);
return TRUE;
}
LONG WINAPI MyExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
ShutdownHandler(0);
return EXCEPTION_EXECUTE_HANDLER;
}
USHORT DefPort = 8182;
int main(int argc, char* argv[])
{
if (argc == 2)
DefPort = atoi(argv[1]);
InitializeCriticalSection(&amp;csProtection);
SetUnhandledExceptionFilter(MyExceptionFilter);
SetConsoleCtrlHandler(ShutdownHandler, TRUE); // process CTRL+C
hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
WSADATA data = {0};
WSAStartup(0x0202, &amp;data);
hListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == hListen)
{
ShutdownHandler(0);
}

SOCKADDR_IN addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(DefPort);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(hListen, reinterpret_cast<PSOCKADDR>(&amp;addr),
sizeof(addr)) == SOCKET_ERROR)
{
ShutdownHandler(0);
}
if (listen(hListen, 256) == SOCKET_ERROR)
ShutdownHandler(0);
SYSTEM_INFO si = {0};
GetSystemInfo(&amp;si);
si.dwNumberOfProcessors<<= 1;
for (int i= 0; i < si.dwNumberOfProcessors; i++)
{
// CreateThread(NULL, NULL, ThreadProc, hIocp, NULL, NULL);
QueueUserWorkItem(ThreadProc, hIocp, WT_EXECUTELONGFUNCTION);
}
cout<<&quot;Startup at&quot;<<TimeNow()<<endl<<&quot;Work on Port&quot;<<DefPort<<endl
<<&quot;Press CTRL+C to Shutdown&quot;<<endl<<endl<<endl;

while(TRUE)
{
int namelen = sizeof(addr);
memset(&amp;addr,0, sizeof(addr));
SOCKET hAccept = accept(hListen, reinterpret_cast<PSOCKADDR>(&amp;addr), &amp;namelen);
if (hAccept != INVALID_SOCKET)
{
cout<<&quot;accept a client:&quot;<<inet_ntoa(addr.sin_addr)<<&quot;:&quot;<<ntohs(addr.sin_port)<<endl;
PPRE_SOCKET_DATA pPreHandleData = new PRE_SOCKET_DATA;
pPreHandleData->hSocket = hAccept;
memcpy(&amp;pPreHandleData->ClientAddr, &amp;addr, sizeof(addr));
CreateIoCompletionPort(reinterpret_cast<HANDLE>(hAccept),
hIocp, reinterpret_cast<DWORD> (pPreHandleData), 0);
PPRE_IO_DATA pPreIoData = new (nothrow) PRE_IO_DATA;
if (pPreIoData)
{
EnterCriticalSection(&amp;csProtection);
gSockDataVec.push_back(pPreHandleData);
gIoDataVec.push_back(pPreIoData);
LeaveCriticalSection(&amp;csProtection);
memset(pPreIoData, 0, sizeof(PRE_IO_DATA));
pPreIoData->IoType = IoRecv;
pPreIoData->DataBuf.len = MAX_BUFFER_SIZE;
pPreIoData->DataBuf.buf = pPreIoData->Buffer;
DWORD dwRecv = 0;
DWORD dwFlags = 0;
WSARecv(hAccept, &amp;pPreIoData->DataBuf, 1,
&amp;dwRecv, &amp;dwFlags,
reinterpret_cast<WSAOVERLAPPED*> (pPreIoData),
NULL);
}
else
{
delete pPreHandleData;
closesocket(hAccept);
}
}
}
return 0;
}

客户端:
#include <WinSock.h>
#include <iostream>
using namespace std;
#pragma comment(lib, &quot;ws2_32.lib&quot;)
int main(int argc, char* argv[])
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &amp;wsaData) != 0)
{
cout<<&quot;WSAStartup error, exit/n&quot;;
return -1;
}

SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8182);
addr.sin_addr.s_addr = inet_addr(&quot;192.168.4.121&quot;);
int ierr = connect(sock, (sockaddr*)&amp;addr, sizeof(addr));
if (ierr != 0)
return -1;

char szBuff[2000] = {0};
char *s = &quot;c://1.txt&quot;;
ierr = send(sock, s, strlen(s)+1, 0);
if (ierr != (strlen(s)+1))
return -1;

int iLen = recv(sock, szBuff, 1024, 0);
if (iLen > 0)
{
for (int i = 0; i < iLen; i++)
cout<<szBuff;
}
else
cout<<&quot;recv() error&quot;<<endl;
return 0;
}
 
谢谢高手们的代码 我也刚学完成端口 感觉还是有难度的 要做到灵活运用 还需花很多时间
 
顶部