Posted on 2012-01-20 15:04
Seed-L 阅读(306)
评论(1) 编辑 收藏 引用
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*/