一.前言 数据通讯的重要性是不言而喻的,特别是在写程序的过程中掌握数据通讯技术,了解各种通讯的方法及其优缺点是很重要的。调制解调器是目前进行远程通讯的一种重要工具,基于调制解调器的应用越来越多,TAPI就是Microsoft及Intel公司联合开发的,基于MODEM,电话线进行数据传输的一种应用程序接口,TAPI既适用于BC++也适用于VC++。 近一段时间我一直使用TAPI实现两台机器间用MODEM,电话线之间进行数据传输,现总结如下: Windows95/98基于API的通信手段大致分为以下几种: 1. 基于TCP/IP协议的WinSock API,可实现局域网上或互联网上的微机通信; 2. 基于进程之间的通信技术:动态数据交换(DDE); 3. 基于直接电缆连接的通信技术,可直接操作串行口、交行口以及远红外线接口; 4. 基于电话线路的通信应用程序接口(TAPI),可方便地控制调制解调器。 二.通讯过程 TAPI的基本通讯过程包括如下几步: 步骤 说明 初始化TAPI环境 使用lineInitialize函数初始化TAPI32.DLL,获得TAPI句柄。此时必须指定一个回调函数,由此回调函数处理消息。 应答方初始化 1. 通过lineNegotiateAPIVersion确定使用TAPI的版本; 2. 用lineOpen打开线路,得到HLINE句柄,dwPrivileges函数必须使用LINECALLPRIVILEGE_MONITOR+LINECALLPRIVILEGE_OWNER,与呼叫方不同; 呼叫方进行呼叫 1. 通过lineNegotiateAPIVersion确定使用TAPI的版本; 2. 用lineOpen打开线路,得到HLINE句柄,dwPrivileges函数必须使用LINECALLPRIVILEGE_NONE; 3. 创建拨号参数,类型为:LPLINECALLPARAMS; 4. 用lineMakeCall或lineDial进行拨号,两者的区别是,使用前者是还没有HCALL句柄的情况下,使用此拨号可得到HCALL句柄,而lineDial是在HCALL句柄已得到的情况下使用。 回调函数的处理 回调函数对消息进行处理,最主要的消息包括: 1. LINECALLSTATE_OFFERING:此函数由应答方捕获,得到此消息表明已接收到拨号方的呼叫,应进行回应,回应的格式为:lineAnswer( (HCALL)dwDevice,NULL,0 ); 2. LINECALLSTATE_BUSY, LINECALLSTATE_IDLE, LINECALLSTATE_SPECIALINFO:在拨号或应答过程中出现错误,应断接; 3. LINECALLSTATE_DISCONNECTED:某一方断接,此时可以判断到底是哪一种原因造成断接,同时也应调用断接函数; 4. LINECALLSTATE_CONNECTED:已连接上并建立线路,此时应调用lineGetID函数得到MODEM的句柄,必要时应清空MODEM缓冲区,以便开始进行数据的传输。 断接 1. 任何一方可以调用linDrop(HCALL)来停止呼叫,该函数将会发送LINECALLSTATE_IDLE消息给回调函数 2. 当任何一方收到LINECALLSTATE_IDLE消息时应调用lineDeallocateCall(hCall)来释放掉占用的呼叫资源; 3. 当收到LINECALLSTATE_DISCONNECTED 消息时应使用lineClose(HLINE)释放由lineOpen 分配的资源,调用lineShutDown(HLINEAPP)释放为线路设备分配的资源 数据的发送与接收 使用ReadFile与WriteFile函数发送与接收数据,共中的文件句柄好是MODEM句柄。 三.数据发送与传输 ReadFile与WriteFile并非TAPI的函数集成员函数,TAPI并不提供数据传输的函数。 (一)函数调用格式: ReadFile的调用格式为: ReadFile(文件句柄,发送的数据的缓冲区地址,欲发送多少字节数据,实际发送了多少字节数据,一个指向OVERLAPPED结构的指针); 1. 文件句柄:此处即为MODEM句柄; 2. 发送数据的缓冲区地址:可为char *或LPDWORD格式,传送的数据为二进制格式; 3. 欲发送多少字节数据:用户指定,但也可以使用ClearCommError函数来测到当前MODEM缓冲区有多少字节数据,然后有多少读多少。 4. 实际发送了多少字节数据:返回值; 5. OVERLAPPED结构的指针: (二)传输模式 在95/98下:ReadFile与WriteFile有两种模式,一种是等待模式,一种是非等待模式。 等待模式的格式为: DWORD len; ReadFile(g_hCommFile,lpBuffer,256,&len,NULL); 此时必须完成了读写操作函数才会返回,实际读写的字节数在len中,最后一个参数必须为NULL。 非等待模式的格式为: OVERLAPPED myOVLP = {0, 0, 0, 0, NULL}; myOVLP.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); ReadFile(g_hCommFile,lpBuffer,256,NULL,&myOVLP); GetOverlappedResult(g_hCommFile,&myOVLP,&len,TRUE) CloseHandle(myOVLP.hEvent); 此时ReadFile函数会马上返回,第四个参数会被忽略,实际读写的字节数由GetOverlappedResult函数返回,如果读写成功,此函数为返回非0值,最后一个函数为TRUE,表示等待方式,为FALSE表示非等待。 在NT下必须使用OVERLAPPED参数,调用格式与95/98下的非等待方式类似。 (三)如何保证数据传输的正确性 在等待模式下,如果读写操作不完成,函数就会一直处于挂起状态; 在非等待模式下,函数调用后会马上返回,此时应调用GetOverlappedResult函数来判断,如果此函数返回FALSE,就应进一步判断,例如: WriteFile(g_hCommFile,lpBuffer,256,NULL,&myOVLP); while(!GetOverlappedResult(g_hCommFile,&myOVLP,&len,TRUE)) { dwError=GetLastError(); if(dwError == ERROR_IO_INCOMPLETE) continue; else 出错; } (四)如何提高传输性能 因为MODEM的缓冲区毕竟非常有限,如果某一方出现阻塞,另一方就不得不陷入等待之中,而且双方之间的协调会使速率非常之低,此时可以采用缓冲区的办法,发送与接收双方都开辟一个缓冲区(应是循环的),数据直接写入缓冲区中,然后用线程的方法,发送方不断的判断缓冲区中是否有数据,如果有发送方就发送到MODEM中,接收方则不断判断MODEM中是否有数据,如果有就读入缓冲区中。 就MODEM传输速度,我使用的环境是内线,没有通过交换机,满载的情况下每秒在6K字节/秒左右。 四.消息的处理 TAPI由回调函数进行消息处理,回调函数在初始化TAPI时创建,消息的处理在TAPI的使用过程中是至关重要的。以下是一些主要的消息: 1. LINECALLSTATE_IDLE 没有呼叫,为空,此时应断接,释放掉占用的资源; 2. LINECALLSTATE_BUSY 线路忙或设备忙,此时应断接,释放掉占用的资源; 3. LINECALLSTATE_SPECIALINFO 特别的消息,此时应断接,释放掉占用的资源; 4. LINECALLSTATE_OFFERING 应答方已收到呼叫方信号,此时应进行应答,调用lineAnswer函数; 5. LINECALLSTATE_CONNECTED 已连接成功,此时可进行数据的传输,但必须先得到MODEM的句柄; 6. LINECALLSTATE_DISCONNECTED 已断接,此时应释放掉占用的资源。 |