UDP协议开发小结

    xiaoxiao2021-03-25  58

    //局域网内的聊天软件,显示局域网内在线用户 //没有客户端和服务器之分 enum{UPORT = 8880};//固定连接端口 enum{ //命令号 SEND_HELLO = 0x1234, //局域网内某用户上线后,会向其他主机发送该命令(打招呼) REPL_HELLO, //收到打招呼信息后,回复该命令 SEND_BYE, //当某主机下线后,会向其他主机发送该命令 SEND_TEXT, //发送文字的命令 SEND_FILE //发送文件的命令 }; struct SInfo //存放本机的信息,主机名,用户名,IP地址,用户组 { char sHost[16]; char sName[16]; char sIp[16]; char sGroup[16]; }; struct SPack//作为接受/发送的数据包,命令号+数据,防止丢包 { int nCmd; char sData[1020]; }; struct SText//文字信息,包含主机名和文字 { char sName[16]; char sText[256]; }; //APP初始化程序中需要完成的事情: SInfo & info = m_info; //定义一个本机信息结构体的引用 gethostname(info.sHost,sizeof(info.sHost));//获取本机名 //int gethostname (char *name, int namelen ); 缓冲区以及其长度,返回0表示获取成功,返回SOCKET_ERROR表示失败,WSAGetLastError(),获取失败代码 hostent* pHost = gethostbyname(info.sHost);//获取与该主机名相对应的主机信息,返回一个结构体 /* struct hostent { char FAR *h_name; //主机名 char FAR *FAR *h_aliases; //域名的别名 short h_addrtype; //返回地址的类型IP4 or IP6 short h_length; //地址字节数4 or 6 char FAR * FAR * h_addr_list; //主机地址列表,该值是网络字节序,一般取第一个 }; */ in_addr* addr = (in_addr*)pHost->h_addr;//调用返回结构体中的IP地址,该值是网络字节序 //这里用了类型转换,h_addr_list[0] 就是h_addr,他指向的in_addr结构体的地址值,他是个指针的指针 strcpy(info.sIp,inet_ntoa(*addr));//网络字节转字符串,并复制到本机信息结构体中 DWORD dw = sizeof(info.sName); GetUserName(info.sName,&dw);//获取本机用户名 /* BOOL GetUserName( LPTSTR lpBuffer, // name bufferm用户名缓冲区 LPDWORD nSize // size of name buffer 缓冲区大小的地址,DWORD型 ); */ CString sIP; SPack pack = {SEND_HELLO}; memcpy(pack.sData,&info,sizeof(SInfo));//将info里的信息放入发送包里 //关于复制内存 //memcpy、strcpy 都是拷贝内存,但前者根据长度,后者拷贝到'\0'while(i<255) //向局域网内其他253个主机打招呼,该方式运行速度较慢 while(i<255) { sIP.Format("192.168.1.%d",i);//若接收方没有该软件接受则接受程序会返回一个错误码 m_sock.SendTo(&pack,sizeof(int)+sizeof(SInfo),UPORT,sIP); ++i; } /* int SendTo( const void* lpBuf, //发送数据的缓冲区 int nBufLen, //缓冲区待发送的字节数 UINT nHostPort, //端口号 LPCTSTR lpszHostAddress = NULL, //连接到的套接字的网络地址 int nFlags = 0 ); //设置函数调用方式 */ //该方式运行速度快 sIP = info.sIp;//通过广播地址发送。 int i=sIP.ReverseFind('.');//找到字符串中最后一个.号的位置,从0 开始192.168.1.即9,这样做的前提是不知道IP地址 sIP = sIP.Left(i+1)+"255";//选取该字符串左十位,与"255"合并,构成广播地址。 m_sock.SendTo(&pack,sizeof(int)+sizeof(SInfo),UPORT,sIP);//发送给广播地址,广播地址群发给局域网内所有主机 //对话框初始函数,设置列表控件的风格: m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);//整行选取+网格线 //信息接收函数,重载了基类CSocket::OnReceive(nErrorCode) void CSocketu::OnReceive(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class SPack pack={0};//定义一个pack接受数据 CString sIP; UINT nPort; int nRet = ReceiveFrom(&pack,sizeof(pack),sIP,nPort);//第二个参数为缓冲区的长度,有别于发送函数 if(nRet <= 0)//接受失败,若对方主机没有正确接收的数据包(对方主机没有安装该软件)也会返回也一个错误码:c10054 { int nError = GetLastError(); return; } CUdpsocketDlg *pDlg = (CUdpsocketDlg*)AfxGetMainWnd(); switch(pack.nCmd) { case SEND_HELLO://如果是有主机上线的消息 { pDlg->InsertHost(pack);//插入列表控件 SInfo& info = (SInfo&)pack.sData; if(!strcmp(info.sHost,theApp.m_info.sHost))//判断是否是本机发来的消息 return; pack.nCmd = REPL_HELLO;//将发来的消息包中的内容替换成本机的信息 info = theApp.m_info; SendTo(&pack,sizeof(int)+sizeof(SInfo),nPort,sIP);//替换之后发回去 } break; case REPL_HELLO: pDlg ->InsertHost(pack);//接受到回复后插入列表 break; case SEND_BYE: pDlg->RemoveHost(sIP);//有用户退出则移除他在列表控件上的名字 break; case SEND_TEXT: pDlg->OnText(pack); break; } // CSocket::OnReceive(nErrorCode); } //往列表控件中插入上线主机信息 void CUdpsocketDlg::InsertHost(SPack &pack) { SInfo& info = (SInfo&)pack.sData; //将SDate强制转换为SInfo的一个引用,sDate里就存了一个info的结构体大小的内容 CString sHost = info.sHost; int i = -1; int nCount = m_list.GetItemCount();//获取控件中信息行数 while(++i<nCount)//遍历列表,如果当前列表存在该主机名,则退出 { if(m_list.GetItemText(i,0) == sHost) break; } if(i == nCount)//若不存在,则插入 { m_list.InsertItem(i,info.sHost); m_list.SetItemText(i,1,info.sName); m_list.SetItemText(i,2,info.sIp); } } //双击列表控件中的信息,弹出一个聊天对话框 void CUdpsocketDlg::OnDblclkList(NMHDR* pNMHDR, LRESULT* pResult) //定义一个双击的消息响应 { // TODO: Add your control notification handler code here NM_LISTVIEW* p = (NM_LISTVIEW*) pNMHDR; //参数pNMHDR中包含了点击项的在列表中的位置信息,转为NM_LISTVIEW*类型 /* typedef struct tagNMLISTVIEW { NMHDR hdr; int iItem; int iSubItem; UINT uNewState; UINT uOldState; UINT uChanged; POINT ptAction; LPARAM lParam; } NMLISTVIEW, FAR *LPNMLISTVIEW; */ int nSel = p->iItem; //获取选中的行数 if(nSel<0) //未选中 return; CChatDlg *pDlg = (CChatDlg *)m_list.GetItemData(nSel);//该函数获取列表中该项信息的附加信息,一旦对该项有过操作,该附加信息就不为空 if(!pDlg) //若没有点击过该项 { pDlg = new CChatDlg; // 创建一个聊天对话框对象 SInfo& info = pDlg->m_info; m_list.GetItemText(nSel,0,info.sHost,sizeof(info.sHost)); //获取该项的中每一列的内容,放进info中 m_list.GetItemText(nSel,1,info.sName,sizeof(info.sName)); m_list.GetItemText(nSel,2,info.sIp,sizeof(info.sIp)); pDlg->Create(IDD_CHATDLG,GetDesktopWindow());//创建非模式对话框(该对话框未关闭前可以对其他对话框的操作),父窗口为桌面窗口 //GetDesktopWindow()返回桌面窗口的句柄 m_list.SetItemData(nSel,(DWORD)pDlg);//将该信息附加到对应项上 } pDlg->ShowWindow(SW_SHOW);//以当前大小和位置显示对话框 pDlg->SetForegroundWindow();//将该窗口设置为前台窗口 *pResult = 0; } //发送消息消息响应 void CChatDlg::OnOK() { // TODO: Add extra validation here SPack pack={SEND_TEXT}; SText& text = (SText &)pack.sData;//强制转为text引用,因为发送文字只需SText结构体大小 int nSel = GetDlgItemText(IDC_SEND,text.sText,sizeof(text.sText));//返回复制到缓冲区的文字个数 if(nSel<=0)//获取发送控件里的文字 { AfxMessageBox("不能发送空文字!"); return; } strcpy(text.sName,theApp.m_info.sHost); theApp.m_sock.SendTo(&pack,sizeof(int)+sizeof(SText),UPORT,m_info.sIp);//发送本机名和文字 COleDateTime time = COleDateTime::GetCurrentTime(); CString str;//将自己发的文字显示界面 str.Format("你对 %s 说: (d:d:d) \r\n%s\r\n",m_info.sHost, time.GetHour(),time.GetMinute(),time.GetSecond(),text.sText); CEdit *pEdit = (CEdit*)GetDlgItem(IDC_LIST);//获取列表控件的句柄 pEdit->SetSel(pEdit->GetWindowTextLength(),-1); pEdit->ReplaceSel(str); SetDlgItemText(IDC_SEND,""); GetDlgItem(IDC_SEND)->SetFocus(); //CDialog::OnOK(); } //接受到文字时的处理函数 void CUdpsocketDlg::OnText(SPack &pack) { SText &text = (SText&)pack.sData;//强制引用 int i = -1; int nCount = m_list.GetItemCount(); while(++i<nCount) { if(m_list.GetItemText(i,0) == text.sName)//如果列表中存在该主机 { CChatDlg * pDlg = (CChatDlg*)m_list.GetItemData(i);//获取该项的附件信息 if(!pDlg)//如果消息为空,即没有打开过该聊天窗口 { pDlg = new CChatDlg;//创建新的对话框 SInfo& info = pDlg->m_info; m_list.GetItemText(i,0,info.sHost,sizeof(info.sHost)); m_list.GetItemText(i,1,info.sName,sizeof(info.sName)); m_list.GetItemText(i,2,info.sIp,sizeof(info.sIp)); pDlg->Create(IDD_CHATDLG,GetDesktopWindow()); m_list.SetItemData(i,(DWORD)pDlg); } pDlg->ShowWindow(SW_SHOW); pDlg->SetForegroundWindow(); pDlg->FlashWindow(true);//使通知栏窗口闪烁 CString str; COleDateTime time = COleDateTime::GetCurrentTime(); str.Format("%s 对你说 %d:%d:%d \r\n %s \r\n",text.sName,time.GetHour(),time.GetMinute(),time.GetSecond(),text.sText); CEdit* pEdit = (CEdit *)pDlg->GetDlgItem(IDC_LIST1); pEdit->SetSel(pEdit->GetWindowTextLength(),-1); pEdit->ReplaceSel(str); return; } } } //App退出函数 int CUdpsocketApp::ExitInstance() //退出进程函数 { SPack pack = {SEND_BYE}; CString sIP = m_info.sIp; int i = sIP.ReverseFind('.'); sIP = sIP.Left(i+1)+"255"; m_sock.SendTo(&pack,sizeof(int),UPORT,sIP);//发送BYE消息到局域网内其他主机 return CWinApp::ExitInstance(); } //删除该IP所占的一行 void CUdpsocketDlg::RemoveHost(CString szIP) { int i = 0; int nCount = m_list.GetItemCount(); while(i++<nCount) { CString s = m_list.GetItemText(i,2); if(m_list.GetItemText(i,2) == szIP) { m_list.DeleteItem(i); } } }

     

    转载请注明原文地址: https://ju.6miu.com/read-39663.html

    最新回复(0)