说明:看Netbios程序过程中,总结的开发服务器程序的步骤,相关代码多来自windows网络编程
Netbios主要应用域小型局域网,服务器端(事件模型)开发的步骤如下:
(1)枚举出本机所有的LANA,然后重设每个LANA。
//枚举所有的LANA
int LanaEnum(LANA_ENUM *lenum)
{
NCB ncb;
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command=NCBENUM;//指定要执行的NetBios命令
ncb.ncb_buffer=(PUCHAR)lenum;
ncb.ncb_length=sizeof(LANA_ENUM);
if(Netbios(&ncb)!=NRC_GOODRET)
{
printf("ERROR:NetBios:NCBENUM:%d/n",ncb.ncb_retcode);
return ncb.ncb_retcode;//指定操作的返回代码,在一个异步操作进行期间,函数将该值设为NRC_PENDING
}
return NRC_GOODRET;
}
//重设每个LANA
int ResetAll(LANA_ENUM *lenum,UCHAR ucMaxSession,UCHAR ucMaxName,BOOL bFirstName)
{
NCB ncb;
int i;
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command=NCBRESET;
ncb.ncb_callname[0]=ucMaxSession;
ncb.ncb_callname[2]=ucMaxName;
ncb.ncb_callname[3]=(UCHAR)bFirstName;
for(i=0;i<lenum->length;i++)
{
ncb.ncb_lana_num=lenum->lana[i];
if(Netbios(&ncb)!=NRC_GOODRET)
{
printf("ERROR:Netbios:NCBRESET[%d]:%d/n",ncb.ncb_lana_num,ncb.ncb_retcode);
return ncb.ncb_retcode;
}
}
return NRC_GOODRET;
}
(2)为每个LANA分配一个NCB结构,创建相同数量的人工重置事件对象
NCB *nbArray=(PNCB)GlobalAlloc(GMEM_FIXED|GMEM_ZERO,sizeof(NCB)*lenum.length);
HANDLE *hArray=(HANDLE *)GlobalAlloc(GMEM_FIXED|GMEM_ZERO,sizeof(HANDLE)*lenum.length);
for(i=0;i<lenum.length;i++)
{
hArray[i]=CreateEvent(NULL,TRUE,FALSE,NULL);
nbArray[i].ncb_event=hArray[i];
}
(3)将服务器进程的名字增加到每个LANA的名字表中(这个名字有点儿类似于socket中的端口号)
//向LANA的名字表中增加名字
int AddName(int lana,char *name,int *num)
{
NCB ncb;
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command=NCBADDNAME;
ncb.ncb_lana_num=lana;
memset(ncb.ncb_name,' ',NCBNAMSZ);
strncpy(ncb.ncb_name,name,strlen(name));
if(Netbios(&ncb)!=NRC_GOODRET)
{
printf("ERROR:Netbios:NCBADDNAME[lana=%d;name=%s]:%d/n",lana,name,ncb.ncb_retcode);
return ncb.ncb_retcode;
}
*num=ncb.ncb_num;
return NRC_GOODRET;
}
//将服务进程名增加到每个LANA中
for(i=0;i<lenum.length;i++)
AddName(lenum.lana[i],SERVER_NAME,&num);
(4)在每个LANA上进行异步监听
//监听函数
int Listen(PNCB pncb,int lana,char *name)
{
pncb->ncb_command=NCBLISTEN|ASYNCH;
pncb->ncb_lana_num=lana;
//
//This is the name clients will connect to
//
memset(pncb->ncb_name,' ',NCBNAMSZ);
strncpy((char *)pncb->ncb_name,name,strlen(name));
//
//远程应用(即客户端)名字的第一个字符指定为“*”表示我们希望监听所有的
//客户端连接;如果指定一个客户端名字,则表示我们指监听指定的客户端的链接
//
memset(pncb->ncb_callname,' ',NCBNAMSZ);
pncb->ncb_callname[0]='*';
if(Netbios(pncb)!=NRC_GOODRET)
{
printf("Error:Netbios:NCBLISTEN:%d/n",pncb->ncb_retcode);
return pncb->ncb_retcode;
}
return NRC_GOODRET;
}
//在所有LANA指定的名字上进行监听
for(i=0;i<lenum.length;i++)
Listen(&nbArray[i],lenum.lana[i];SERVER_NAME);
(5)等待客户端连接
while(true)
{
//wait until a client connects
//
dwRet=WaitForMultipleObjects(lenum.length,hArray,FALSE,INFINITE);
if(dwRet==WAIT_FAILED)
{
printf("Error:WaitForMultipleObjects:%d/n",GetLastError());
break;
}
//
//Go through all the NCB structures to see whether more than one
//succeed.if ncb_cmd_plt is not NRC_PENDING,there is a client:create
//a thread ,and hand off a new NCB structure to thread.We need to reuse the orignal
//NCB for other client connections
//
for(i=0;i<lenum.length;i++)
{
if(nbArray[i].ncb_cmd_cplt!=NRC_PENDING)
{
pncb=(PNCB)GlobalAlloc(GMEM_FIXED,sizeof(NCB));
memcpy(pncb,&hArray[i],sizeof(NCB));
pncb->ncb_event=0;
//创建一个线程对建立的链接进行处理
hThread=CreateThread(NULL,0,ClientThread,(LPVOID)pncb,0,&dwThreadId);
CloseHandle(hThread);
//
//将事件对象置为非信号状态,继续监听其他请求
//
ResetEvent(hArray[i]);
Listen(&nbArray[i],lenum.lana[i],SERVER_NAME);
}
}
}
(6)线程函数
//取得远程应用名字
//Format the given NetBIOS name so that it is printable.Any
//unprintable charactors are replaced by a period.The outname
//buffer is the returned string ,which is assumed to be at least
//NCBNAMSZ+1 charactors in length
//
int FormatNetbiosName(char *nbName,char *outname)
{
int i;
strncpy(outname,nbName,NCBNAMSZ);
outname[NCBNAMSZ-1]='/0';
for(i=0;i<NCBNAMSZ-1;i++)
{
//if the charactor isn't printable,replace it with '.'
if(!((outname[i]>=32)&&(outname[i]<126)))
outname[i]='.';
}
return NRC_GOODRET;
}
//
//线程函数
//
//Description:
// This thread takes the NCB structure of a connected session
// and waits for incoming data,which it then sends back to the
// client until hte session is closed
//
DWORD WINAPI ClientThread(PVOID lpParam)
{
PNCB pncb=(PNCB)lpParam;
NCB ncb;
char szRecvBuff[MAX_BUFFER],szClientName[NCBNAMSZ+1];
DWORD dwBufferLen=MAX_BUFFER,dwRetVal=NRC_GOODRET;
//取得远程应用的名字
FormatNetbiosName((char *)pncb->ncb_callname,szClientName);
//
//Send and receive messages until the session is closed
//
while(true)
{
dwBufferLen=MAX_BUFFER;
//接受数据
dwRetVal=Recv(pncb->ncb_lana_num,pncb->ncb_lsn,szRecvBuff,&dwBufferLen);
if(dwRetVal!=NRC_GOODRET)
break;
//将受到的数据原封不动的发送给客户端
szRecvBuff[dwBufferLen]=0;
printf("READ [LANA=%d]:'%s'/n",pncb->ncb_lana_num,szRecvBuff);
dwRetVal=Send(pncb->ncb_lana_num,pncb->ncb_lsn,szRecvBuff,dwBufferLen);
if(dwRetVal!=NRC_GOODRET)
break;
}
printf("Client '%s' on LANA %d disconnected/n",szClientName,pncb->ncb_lana_num);
//
//if the error returned from a read or a write is NRC_SCLOSED,
//all is well;otherwise,some other error occurred,so hang up the
//connection from this side
//
if(dwRetVal!=NRC_SCLOSED)
{
ZeroMemory(&ncb,sizeof(NCB));
ncb.ncb_command=NCBHANGUP;
ncb.ncb_lsn=pncb->ncb_lsn;
ncb.ncb_lana_num=pncb->ncb_lana_num;
if(Netbios(&ncb)!=NRC_GOODRET)
{
printf("Error:Netbios:NCBHANGUP:%d/n",ncb.ncb_retcode);
GlobalFree(pncb);
dwRetVal=ncb.ncb_retcode;
}
}
//
//the NCB structure in is dynamically allocated,so
//delete is before we go.
//
GlobalFree(pncb);
return NRC_GOODRET;
}
//其中好几部都可以放在一个循环中,为了拆分步骤,有多个循环。