1//程序清单2-3 异步事件服务器(Evnbsvr.c)
  2// 编译命令:cl -o Evnbsvr.exe Evnbsvr.c ..\Common\Nbcommon.obj Netapi32.lib
  3//
  4#include <windows.h>
  5#include <stdio.h>
  6#include <stdlib.h>
  7
  8#include "nbcommon.h"
  9#include<string.h>
 10
 11#define MAX_SESSIONS 254
 12#define MAX_NAMES 254
 13#define MAX_BUFFER 2048
 14#define SERVER_NAME "TEST-SERVER-1"
 15
 16
 17NCB *g_Clients=NULL; // Global NCB structure for clients
 18
 19DWORD WINAPI ClientThread(PVOID lpParam) ;
 20
 21int Listen(PNCB pncb, int lana, char *name) ;
 22
 23
 24
 25//PS:/*要补上连接库netapi32.lib,在工程设置里*/
 26
 27
 28
 29//
 30// 函数:main
 31//
 32// 说明:
 33// 初始化NetBIOS 接口,分配一些资源,并且使用事件在每个LANA 上产生异步监听,等待触发
 34// 事件,然后处理客户端连接
 35//
 36int main(int argc, char **argv)
 37{
 38    PNCB pncb=NULL;
 39    HANDLE hArray[64],
 40        hThread;
 41    DWORD dwHandleCount=0,
 42        dwRet,
 43        dwThreadId;
 44    int i,
 45        num;
 46    LANA_ENUM lenum;
 47
 48    printf("主函数开始执行\n") ;
 49
 50    if (LanaEnum(&lenum) != NRC_GOODRET)
 51        return 1;
 52
 53        if (ResetAll(&lenum, (UCHAR)MAX_SESSIONS, (UCHAR)MAX_NAMES,
 54            FALSE) != NRC_GOODRET)
 55            return 1;
 56        // 分配一个NCB 结构体数组
 57        g_Clients = (PNCB)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
 58            sizeof(NCB) * lenum.length);
 59        // 创建事件,为每个LANA 添加服务器名,并且异步监听每个LANA
 60        for(i = 0; i < lenum.length; i++)
 61        {
 62            printf("添加第%d个LANA\n",i+1) ;
 63
 64            hArray[i] = g_Clients[i].ncb_event = CreateEvent(NULL, TRUE,FALSE, NULL);
 65            AddName(lenum.lana[i], SERVER_NAME, &num);
 66            Listen(&g_Clients[i], lenum.lana[i], SERVER_NAME);
 67        }

 68        while (1)
 69        {
 70            // 等待直到客户端连接
 71            printf("开始等待连接\n") ;
 72
 73
 74            dwRet = WaitForMultipleObjects(lenum.length, hArray, FALSE,
 75                INFINITE);
 76            if (dwRet == WAIT_FAILED)
 77            {
 78                printf("ERROR: WaitForMultipleObjects: %d\n",
 79                    GetLastError());
 80                break;
 81            }

 82
 83            printf("有连接了\n") ;
 84
 85            // 遍历所有NCB 结构体,如果ncb_cmd_plt 不是NRC_PENDING,说明还有一个客户端,
 86            // 为它创建线程并为线程分配新的NCB 结构体,我们需要为其它客户端连接重用原来的NCB
 87                for(i = 0; i < lenum.length; i++)
 88                {
 89                    if (g_Clients[i].ncb_cmd_cplt != NRC_PENDING)
 90                    {
 91                        pncb = (PNCB)GlobalAlloc(GMEM_FIXED, sizeof(NCB));
 92                        memcpy(pncb, &g_Clients[i], sizeof(NCB));
 93                        pncb->ncb_event = 0;
 94                        hThread = CreateThread(NULL, 0, ClientThread,
 95                            (LPVOID)pncb, 0&dwThreadId);
 96                        CloseHandle(hThread);
 97                        // 重设句柄并且产生另一个监听
 98                        ResetEvent(hArray[i]);
 99                        Listen(&g_Clients[i], lenum.lana[i], SERVER_NAME);
100                    }

101                }

102        }

103        // 清除
104        for(i = 0; i < lenum.length; i++)
105        {
106            DelName(lenum.lana[i], SERVER_NAME);
107            CloseHandle(hArray[i]);
108        }

109        GlobalFree(g_Clients);
110
111        printf("主函数结束\n");
112        
113        return 0;
114}

115
116// 函数:ClientThread
117// 说明:该线程处理连接会话的NCB 结构,等待进入的数据,会话关闭时,这些数据返回给客户端
118DWORD WINAPI ClientThread(PVOID lpParam)
119{
120    PNCB pncb = (PNCB)lpParam;
121    NCB ncb;
122    char szRecvBuff[MAX_BUFFER],
123        szClientName[NCBNAMSZ + 1];
124    DWORD dwBufferLen = MAX_BUFFER,
125        dwRetVal = NRC_GOODRET;
126    // 发送和接收消息,直到会话关闭
127
128    printf("ClientThread函数开始执行\n");
129
130    FormatNetbiosName(pncb->ncb_callname, szClientName);
131    while (1)
132    {
133        dwBufferLen = MAX_BUFFER;
134        dwRetVal = Recv(pncb->ncb_lana_num, pncb->ncb_lsn,
135            szRecvBuff, &dwBufferLen);
136        if (dwRetVal != NRC_GOODRET)
137            break;
138        szRecvBuff[dwBufferLen] = 0;
139        printf("READ [LANA=%d]: '%s'\n", pncb->ncb_lana_num,
140            szRecvBuff);
141        dwRetVal = Send(pncb->ncb_lana_num, pncb->ncb_lsn,
142            szRecvBuff, dwBufferLen);
143        if (dwRetVal != NRC_GOODRET)
144            break;
145    }

146    printf("Client '%s' on LANA %d disconnected\n", szClientName,
147        pncb->ncb_lana_num);
148    //
149    // 如果从读或写返回NRC_SCLOSED 错误,说明没有问题,如果发生其它错误,则在这端挂起
150        // 连接
151        if (dwRetVal != NRC_SCLOSED)
152        {
153            ZeroMemory(&ncb, sizeof(NCB));
154            ncb.ncb_command = NCBHANGUP;
155            ncb.ncb_lsn = pncb->ncb_lsn;
156            ncb.ncb_lana_num = pncb->ncb_lana_num;
157            if (Netbios(&ncb) != NRC_GOODRET)
158            {
159                printf("ERROR: Netbios: NCBHANGUP: %d\n",
160                    ncb.ncb_retcode);
161                GlobalFree(pncb);
162                dwRetVal = ncb.ncb_retcode;
163            }

164            
165        }

166        // 传递进来NCB 结构是动态分配的空间,要注意删除
167        GlobalFree(pncb);
168
169        printf("ClientThread函数开始结束\n");
170
171        return NRC_GOODRET;
172}

173
174//
175// 函数:Listen
176// 说明:对每个给定LANA 号异步监听,传递进来的NCB 结构已经将它的ncb_event 域设为有效的
177// 窗体事件句柄
178
179int Listen(PNCB pncb, int lana, char *name)
180{
181    pncb->ncb_command = NCBLISTEN | ASYNCH;
182    pncb->ncb_lana_num = lana;
183    //
184    // 客户端要连接的名字
185    printf("Listen函数开始执行\n");
186    //
187    memset(pncb->ncb_name, ' ', NCBNAMSZ);
188    strncpy(pncb->ncb_name, name, strlen(name));
189    memset(pncb->ncb_callname, ' ', NCBNAMSZ);
190    pncb->ncb_callname[0= '*';
191    if (Netbios(pncb) != NRC_GOODRET)
192    {
193        printf("ERROR: Netbios: NCBLISTEN: %d\n", pncb->ncb_retcode);
194        return pncb->ncb_retcode;
195    }

196
197    printf("Listen函数开始结束\n");
198
199    return NRC_GOODRET;
200}

201
202/*
203其中,main 函数的首次循环需要遍历每个可用的LANA 编号,为其增加服务器名字,执行NCBLISTEN
204命令,并同时构建由事件句柄构成的数组。接下来调用WaitForMultipleObjects。在其中一个句柄收
205到信号之前(即进入传信状态之前),这个调用会一直等待。一旦事件控制数组中的一个或多个句柄
206进入传信状态,WaitForMultipleObjects 调用便会完毕,代码会构建一个线程,用它读取进入的消息,
207并将其原封不动回送给客户端。随后,代码会为传信状态的NCB 结构创建一个副本,将其传递进入客
208户端线程。之所以要这样做,是由于我们希望沿用最初的NCB,以执行另一个NCBLISTEN 监听命令。
209要达到这个目的,可重设事件,并针对那个结构再次调用Listen。注意此时没有必要将整个结构都复
210制下来。
211在实际应用中,只需用到本地会话编号(ncb_lsn)和LANA 编号(ncb_lana_num)就可以了。然
212而,要想同时容纳两个值,打算将其都传递给同一个线程参数,那么NCB 结构是一个非常不错的容器。
213事件模型使用的客户端线程与回调模型使用的那个几乎完全相同,只是这里采用了GlobalFree 语句。
214需要注意的是,对前述的两种服务器工作模式来说,都可能存在拒绝为客户端提供服务的情况。
215NCBLISTEN 完成后,在调用回调函数之前,或在事件收到信号之前,都存在少许的延时。只有在
216经历了几个语句之后,服务器才会执行另一个NCBLISTEN 命令。例如,假定服务器在LANA2 上接受了
217一个客户端的连接。随后,在服务器针对同一个LANA 编号执行另一个NCBLISTEN 之前,假如又有一个
218客户端试图建立连接,便会收到一个名为NRC_NOCALL (0x14)的错误信息。这意味着,对指定的名字
219来说,目前尚未在它上面执行NCBLISTEN。要防止此类情况的出现,服务器必须为每个LANA 都执行多
220个NCBLISTEN 命令。从前述两个服务器应用的例子可以看出,异步命令的执行其实是非常容易的。
221ASYNCH 标志可应用于几乎所有NetBIOS 命令。只是要记住,传递给NetBIOS 的NCB 结构有一个全局性
222的范围。
223*/

Feedback

# re: [转载]《Windows网络编程》NetBIOS异步事件服务器模型  回复  更多评论   

2012-02-03 20:16 by 喜多多
新年新气象,天天喜多多!

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


posts - 5, comments - 1, trackbacks - 0, articles - 3

Copyright © Seed-L