局域网多点互连的一种实现方法

                            局域网多点互连的实现方法

     通常局域网内用户的点对点连接多采用C/S架构,这种情况下就必须有一方作为客户端另一方作为服务器端,本文将叙述如何在各用户处于“平级”情况下不依赖服务器所进行的多点互连。

(本文仅罗列相关操作步骤和简单说明,具体函数说明请参考MSDN)

步骤一: 创建广播套接字

    我将创建的整个过程封装在SOCKET NET_CreateBroadCastSocket(char *szAddress)

其中szAddress为本地IP地址的字符串形式具体获得可以通过gethostname()gethostbyname()函数获得。

Winsock的广播必须基于UDP实现,故首先使用SOCK_DGRAM参数创建套接字,然后将其绑定到szAddress的地址上,最后使用setsockopt()设置套接字的广播属性。通常情况下还会使用getsockopt()进行属性验证,本例进行了省略。

函数实现代码如下

    /* 

        功能: 创建广播套接字

        参数: szAddress     绑定IP的字符串形式

        返回值: 具有广播属性基于UDP的套接字

    */

SOCKET NET_CreateBroadCastSocket(char *szAddress)

{

    SOCKET sockBroad;

   

    NET_InitSock();

 

    sockBroad = socket(AF_INET, SOCK_DGRAM, 0);

    if (INVALID_SOCKET == sockBroad)

    {

MessageBox(NULL, "网络初始化失败!", "错误", MB_OK |                  MB_ICONEXCLAMATION);

        PostQuitMessage(0);     //此为配合窗口过程用

    }

    //绑定

    int retVal;

    SOCKADDR_IN addrHost;

    addrHost.sin_family = AF_INET;

    addrHost.sin_addr.S_un.S_addr = inet_addr(szAddress);

    addrHost.sin_port = htons(MAIN_PORT);

    retVal = bind(sockBroad, (SOCKADDR*)&addrHost, sizeof(SOCKADDR));

    if (SOCKET_ERROR == retVal)

    {

        MessageBox(NULL, TEXT("网络错误!"), TEXT("错误"), MB_OK |                                              MB_ICONEXCLAMATION);   

        PostQuitMessage(0);

    }

    //设置广播属性

    BOOL value = TRUE;

    retVal = setsockopt(sockBroad,SOL_SOCKET,SO_BROADCAST, (char *) &value, 1);

    if(SOCKET_ERROR == retVal)

    {

        closesocket(sockBroad);

        WSACleanup();

        return 0;

    }

 

    return sockBroad;

}

注意: 此处广播属性若未设置成功将导致发送数据失败, 此处还要根据所设计数据包的大小设置缓冲区得大小,设置说使用的函数为setsockopt()并使用getsockopt()进行确认设置成功。

步骤二: 创建用户列表,用于保存相关用户信息

//用户信息

typedef struct _USERINFO

{

    char szUsername[MAX_LEN];   //主机名

    char cGroup;            //用户所选分组  用于用户选择 此字段暂时未用

    char szAddress[MAX_LEN];    //IP地址

    struct _USERINFO *next;

}USERINFO;

//用户链表

typedef struct _USERLIST

{

    USERINFO *pUserList;    //用户列表

    int nUser;  //用户数目

}USERLIST;

//用户列表相关操作都是一些简单的链表操作

//创建用户列表

USERLIST *USER_CreateUserList();

//添加用户

void USER_AddUser(USERLIST *pUserlist, USERINFO newUser);

//删除用户

void USER_DeleteUser(USERLIST *pUserlist, USERINFO delUser);

//是否在用户列表

BOOL USER_IsUser(USERLIST *pUserlist, USERINFO isUser);

//销毁用户列表

void USER_DestroyList(USERLIST *pUserlist);

//相关用户初始化代码如下

/*

    此代码片段实现功能,创建用户列表,获取本地用户并将其加入到用户列表中

*/

char hostName[MAX_PATH] = "";

gethostname(hostName, MAX_PATH);    //获取主机名

strcpy(userName, hostName);

HOSTENT *hostInfo = gethostbyname(hostName); //获取主机相关信息

//创建用户列表

pUserlist = USER_CreateUserList();

//将自身加入用户列表

USERINFO newUser;

memset(&newUser, 0, sizeof(USERINFO));

strcpy(newUser.szAddress, inet_ntoa(*(in_addr *)hostInfo->h_addr));//主机IP地址

strcpy(newUser.szUsername, userName);

 

USER_AddUser(pUserlist, newUser);

步骤三: 使用异步I/O基于windows消息的select模型

The WSAAsyncSelect function requests Windows message-based notification of network events for a socket.

int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);

此处我们感兴趣的网络时间包括:FD_CLOST FD_READ

wMsg 为用户自定义消息 UM_MAINSOCK

在创建广播套接字后我们注册相应的网络事件

//相关代码解释如下

//注册网络事件消息

……

#define UM_MAINSOCK           WM_USER + 1

……

int retVal;

retVal = WSAAsyncSelect(sockMain, hwndMain, UM_MAINSOCK, FD_READ | FD_CLOSE);

if (SOCKET_ERROR == retVal)

{

    MessageBox(hwndMain, TEXT("网络错误!"), TEXT("错误"),

                MB_OK | MB_ICONEXCLAMATION);

    PostQuitMessage(0); //配合窗口话过程

}  

……

//消息相应

case UM_MAINSOCK:

        {

            if (WSAGETSELECTERROR(lParam))  //是否有错误

            {

                closesocket(sockMain);

                return FALSE;

            }

           

            switch (WSAGETSELECTEVENT(lParam))  //处理相关时间

            {

            case FD_READ:   //有可接受的数据包

                {

                    int recvLen = sizeof(SOCKADDR);

                    SOCKADDR_IN addrRecv;

                   recvfrom(sockMain,recvBuf,MAX_RECVLEN,0,(SOCKADDR*)&addrRecv,

                        &recvLen);

                    //获取数据包内容

                    USERINFO userInfo;;    

                    userInfo = *((USERINFO *)recvBuf);  //接受到的系统请求信息

 

                    if (!USER_IsUser(pUserlist, userInfo))  //保证用户为加入

                    {

                        USER_AddUser(pUserlist, userInfo);     

sendto(sockMain,(char*)&LocalUserInfo,sizeof(USERINFO), 0,(SOCKADDR *)&addrBroad, sizeof(SOCKADDR));

//此操作保证无论用户登录时间如何都能够获得当前所有和连接用户

                    }

                   }

                   Break;

            case FD_CLOSE:

            {

                closesocket(sockMain);

            }

            break;

            }

}

break;

注意:此处在接受用户数据包时仅简单的将其加入到用户列表,并未牵扯到用户离开时的删除工作,要完成这项工作需要对数据包进行重新包装,并对相关标识字段进行处理。

     用户的唯一标识符为IP地址

步骤四: 广播用户登录信息

//广播登录消息         

addrBroad.sin_family = AF_INET;

addrBroad.sin_addr.S_un.S_addr = htonl(INADDR_BROADCAST); //广播的地址

addrBroad.sin_port = htons(MAIN_PORT);

//登录数据包设置   

strcpy(LocalUserInfo.szUsername, userName);

strcpy(LocalUserInfo.szAddress, newUser.szAddress);

//广播登录消息   此消息允许不成功

sendto(sockMain, (char *)&reqPacket, sizeof(REQPACKET), 0, 

        SOCKADDR *)&addrBroad, sizeof(SOCKADDR));

               

至此,局域网内多点互联基本工作已经完成,在此基础上可以完成局域网聊天室或简单IM程序,作为网络编程,此处要多注意相关函数调用是错误的处理,怎样才能够保证网络连接各种情况的正确性,同时要根据应用需求设置合理的缓冲区大小,否则可能发生未知错误。

以此为基础可以实现   点对点,点对多,多对点通信,当然这需要的适当数据包设计。以及相应的识别处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值