资源描述
评阅教师评语:课程设计成绩考勤成绩实做成绩报告成绩总评成绩指导教师签名:综合课程设计设 计 报 告论文题目: 基于Socket的即时通讯系统 学院(系): 电子信息与自动化学院 班 级: 721 学生姓名: 学号 指导教师: 时间: 2011 年 6月 7日 到 2011 年 6 月 17日一、 设计目的通过综合课程设计,使学生能够运用数字信号处理、信号与系统、通信原理、面向对象的程序设计、计算机通信网、通信协议开发及应用等课程的知识来设计一个基于Socket的即时通讯系统,培养学生的动手能力以及分析问题、解决问题的能力。二、 设计内容一个基于Socket的即时通讯系统的多人聊天软件三、 设计要求(一)基本要求1 熟练掌握面向对象的程序设计方法;2 实现点对点通讯,能进行文字对话传输,包括客户端与服务器端;3 能对系统参数进行配置。(二)提高要求1、实现文件、图片传输;2、语音对话(两人及两人以上);3、友好的对话界面。四、 设计原理1 TCP/IP简介 TCP/IP的历史要追溯到70年代中期,当时ARPA为了实现异种网之间的互连(interconnection)与互通(intercommunication),大力资助网间网技术的研究和开发,于1977年到1979年推出目前形式的TCP/IP体系结构和协议规范.到今天,TCP/IP技术以及Internet网间网已经为广大计算机工作者,机算机厂商和机算机用户所接受.据统计,到1990年,Internet以包含遍布欧美的五千个活动网络,超过三十万台机算机.作为一种事实上的工标准,TCP/IP技术方兴未艾.2 TCP/IP的网络分层结构 对TCP/IP协议来说,TCP提供传输层服务,IP提供网络层服务.TCP/IP协议组(或Internet协议组)的分层结构及其与OSI模型的对应关系如图4所示.图中有关协议的名称及其基本含义如下: (1) TCP. 为传输控制协议(Transmission Control Protocol).它是提供给用户进程的一个可靠的全双工字节流的面向连接的协议.大多数Internet应用程序使用TCP.因为TCP使用IP,所以整个Internet协议组也常称为TCP/IP协议组. (2) UDP. 为用户数据报协议 (User Datagram Protocol). (3) ICMP. 为网间报文控制协议 (Internet Control Message Protocol). (4) IP. 网间协议 (Internet Protocol). IP协议是为TCP,UDP和ICMP提供分组发送服务协议. (5) ARP. 地址转换协议. (6) RARP. 反向地址转换协议.3 Socket编程界面 (1) Socket 原理 Socket编程界面由4BSD UNIX首先提出,目的是解决网间网进程通信问题.Socket接口为进程间通信提供了一种新的手段,它不但能用于同一机器中的进程之间的通信,而且支持网络通信功能.Socket具有类型,反应了对用户透明的通信特性. 一个完整的Socket连接用一个相关描述: 协议,本地地址,本地端口,远地地址,远地端口 Socket 是面向客户-服务器模型而设计的,针对客户和服务器程序提供不同的Socket系统调用. (2) Socket系统调用 不管Socket内部机制如何,它提供给应用程序员的最终界面是一组系统功能调用.下面,我们一一给出重要的Socket系统调用. 1. 创建 Socket - socket() 调用格式如下: sockid = socket (af,type,protocol) af : 地址族,指本socket所用地址类型. type : 类型,指创建socket的应用程序所希望的通信服务器类型. protocol : 协议,指本socket请求的协议. 2. 指定本地地址 - bind()调用 bind()将本地socket地址与所创建的socket联系起来,即将本socket地址赋予socket,以指定本地半相关.bind()的作用相当于给socket命名,调用格式为: bind (sockid,localaddr,addrlen) sockid : socket号. localaddr : 本地socket地址. addrlen : 地址长度. 3. 建立socket连接 - connect () 与 accept ()调用 这两个系统调用用于完成整个相关的建立.其中connect用于建立连接 .调用格为: connect (sockid,destaddr,addrlen) destaddr : 指向对方socket地址(信宿地址)结构的指针. accept : 用于面向连接的服务器,其调用格式为: newsock = accept (sockid,clientaddr,paddrlen) clientaddr : 指向客户socket地址指针. paddrlen : 客户socket地址长度. 4. listen() 调用 此调用用于面向连接服务器,表明它愿意接收连接,listen()在accept()之前调用,格式为: listen (sockid,quelen) quelen : 请求队列长度. 5. 发送数据 - write(),writev(),send()与sendto(),sendmsg() 用于socket数据发送的系统调用一共有五个,其中三个,write(),writev()和send()用于面向连接传输,其余两个用于无连接传输.面向连接的调用可以不指定信宿地址,而无连接的调用必须指定.假如无连接socket的双方均调用过connect(),可以认为是建立有连接的socket,也可以面向连接调用发送数据. 三个面向连接调用三者的格式大致相同: write (sockid,buff,bufflen) : 缓冲发送 writev (sockid,iovector,vectorlen) : 集中发送 send (sockid,buff,bufflen,flags) : 可控缓冲发送 其中buff指向发送缓冲区的指针,bufflen是发送缓冲区大小. 用于无连接数据发送的调用有两个: sendto (sockid,buff,bufflen,flags,dsadd,addrlen) sendmsg (sockid,message,flags):可控集中无连接发送. 6. 接收数据 - read(),readv(),recv()与recvfrom(),recvmsg() 接收数据与发送数据系统调用是一一对应的,两者参数的最大区别是,前者buffer是一个指针,其所指单元初值为欲读数据长度,调用后的值是实际读出的值.4 客户-服务器模型的Socket实现框架 1)客户-服务器模型时序图 下图是面向连接客户-服务器模型的典型时序图 服务器 客户 socket() socket() bind() bind() listen() accept() 等待客户连接请求 阻塞 connect() read() write() 2)服务器socket地址的确定在客户-服务器模型中,所有的作用者都是客户首先发起的(如连接请求,服务请求等),因此客户必须要知道服务器socket地址,另外,客户调用服务器之前,可以在命令行中给出服务器所在主机的域名,根据域名可以获得服务器主机的地址,系统调用为:hp=gethostbyname(host).其中host可以是服务器主机域名,返回hp是一个指向主机地址结构的指针。五、 软件设计(附程序流程图、源程序清单)1. 程序流程图2.源程序清单void CChatRoomDlg:DlgAllInit()CheckRadioButton(IDC_RADIO_CLIENT, IDC_RADIO_SERVER, IDC_RADIO_CLIENT);SetDlgItemText(IDC_IP_ADDR, _T(127.0.0.1); / 初始化ip地址为本机地址。SetDlgItemText(IDC_CONNECT_PORT, _T(5566); / 初始化端口。SetDlgItemText(IDC_LISTEN_PORT, _T(5566);EnableWindow(IDC_STOP_CLIENT, FALSE);EnableWindow(IDC_LISTEN_PORT, FALSE);EnableWindow(IDC_STOP_SERVER, FALSE);EnableWindow(IDC_START_SERVER, FALSE);EnableWindow(IDC_STATIC_LISTEN_PORT, FALSE); / 初始化按键启用or禁用。EnableWindow(IDC_SENDMSG, FALSE);BOOL CChatRoomDlg:EnableWindow(UINT uID, BOOL bEnable)return GetDlgItem(uID)-EnableWindow(bEnable);void CChatRoomDlg:ExtendDiaog(BOOL bShow)static CRect m_DlgRectLarge(0, 0, 0, 0);static CRect m_DlgRectSmall(0, 0, 0, 0);static CRect m_GroupRectLarge(0, 0, 0, 0);static CRect m_GroupRectSmall(0, 0, 0, 0); / 设置 窗口大小 if ( m_DlgRectLarge.IsRectNull() ) GetWindowRect(&m_DlgRectLarge);m_DlgRectSmall = m_DlgRectLarge;m_DlgRectSmall.right -= 220;:GetWindowRect(GetDlgItem(IDC_FRAME)-GetSafeHwnd(), &m_GroupRectLarge);m_GroupRectSmall = m_GroupRectLarge;m_GroupRectSmall.right -= 220; / 设置 窗口 伸缩大小范围if ( bShow ) bShowAll = TRUE;SetWindowPos(NULL, 0, 0, m_DlgRectLarge.Width(), m_DlgRectLarge.Height(), SWP_NOZORDER | SWP_NOMOVE);:SetWindowPos(GetDlgItem(IDC_FRAME)-GetSafeHwnd(), NULL, 0, 0, m_GroupRectLarge.Width(), m_GroupRectLarge.Height(), SWP_NOZORDER | SWP_NOMOVE);elsebShowAll = FALSE;SetWindowPos(NULL, 0, 0, m_DlgRectSmall.Width(), m_DlgRectSmall.Height(), SWP_NOZORDER | SWP_NOMOVE);:SetWindowPos(GetDlgItem(IDC_FRAME)-GetSafeHwnd(), NULL, 0, 0, m_GroupRectSmall.Width(), m_GroupRectSmall.Height(), SWP_NOZORDER | SWP_NOMOVE);void CChatRoomDlg:OnBnClickedNetset()if ( bShowAll ) ExtendDiaog(FALSE);elseExtendDiaog(TRUE); / 设置按键“网络设置”的作用void CChatRoomDlg:OnBnClickedStartServer()m_hListenThread = CreateThread(NULL, 0, ListenThreadFunc, this, 0, NULL);void CChatRoomDlg:ShowMsg(CString strMsg)m_MsgEdit.SetSel(-1, -1);m_MsgEdit.ReplaceSel(strMsg+_T(rn);void CChatRoomDlg:RemoveClientFromArray(CClientItem in_Item)for( int idx = 0; idx ) + strMsg;ShowMsg(strMsg);SendClientsMsg(strMsg);else if (m_bIsServer = FALSE) CString strTmp = _T(张智超的客户端:) + strMsg;ShowMsg(strTmp);int iSend = send(m_ConnectSock, (char *)strMsg.GetBuffer(), strMsg.GetLength()*sizeof(TCHAR), 0);strMsg.ReleaseBuffer();elseAfxMessageBox(_T(请您先进入聊天室!);SetDlgItemText(IDC_INPUT_MSG, _T(); / socket 基本应用void CChatRoomDlg:OnBnClickedStartClient()m_hConnectThred = CreateThread(NULL, 0, ConnectThreadFunc, this, 0, NULL);void CChatRoomDlg:SendClientsMsg(CString strMsg, CClientItem *pNotSend)TCHAR szBufMAX_BUF_SIZE = 0;_tcscpy_s(szBuf, MAX_BUF_SIZE, strMsg);for( INT_PTR idx = 0; idx m_Socket != m_ClientArray.GetAt(idx).m_Socket | pNotSend-hThread != m_ClientArray.GetAt(idx).hThread |pNotSend-m_strIp != m_ClientArray.GetAt(idx).m_strIp) send(m_ClientArray.GetAt(idx).m_Socket, (char *)szBuf, _tcslen(szBuf)*sizeof(TCHAR), 0);void CChatRoomDlg:OnEnChangeInputMsg()CString strMsg;GetDlgItemText(IDC_INPUT_MSG, strMsg);if ( strMsg.IsEmpty() ) EnableWindow(IDC_SENDMSG, FALSE);elseEnableWindow(IDC_SENDMSG);void CChatRoomDlg:StopClient()bShutDown = TRUE;DWORD dwRet = WaitForSingleObject(m_hConnectThred, 1000);if ( dwRet != WAIT_OBJECT_0 ) TerminateThread(m_hConnectThred, -1);closesocket(m_ConnectSock);m_hConnectThred = NULL;m_ConnectSock = INVALID_SOCKET;m_bIsServer = -1;bShutDown = FALSE;void CChatRoomDlg:StopServer()UINT nCount = m_ClientArray.GetCount();HANDLE *m_pHandles = new HANDLEnCount+1;m_pHandles0 = m_hListenThread;for( int idx = 0; idx nCount; idx+ ) m_pHandlesidx+1 = m_ClientArray.GetAt(idx).hThread;bShutDown = TRUE;DWORD dwRet = WaitForMultipleObjects(nCount+1, m_pHandles, TRUE, 1000);if ( dwRet != WAIT_OBJECT_0 ) for( INT_PTR i = 0; i GetWindowRect(&mRect);pt = mRect.BottomRight();pt.y = mRect.top+10;mMenu.LoadMenu(IDR_MENU1);pMenu = mMenu.GetSubMenu(0);pMenu-TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);BOOL CChatRoomDlg:TrayMyIcon(BOOL bAdd)BOOL bRet = FALSE;NOTIFYICONDATA tnd;tnd.cbSize = sizeof(NOTIFYICONDATA);tnd.hWnd = GetSafeHwnd();tnd.uID = IDR_MAINFRAME;if ( bAdd = TRUE ) tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;tnd.uCallbackMessage = WM_TRAYICON_MSG;tnd.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME);_tcscpy_s(tnd.szTip, sizeof(tnd.szTip), _T(聊天室v1.0);ShowWindow(SW_MINIMIZE);ShowWindow(SW_HIDE);bRet = Shell_NotifyIcon(NIM_ADD, &tnd);elseShowWindow(SW_SHOWNA);SetForegroundWindow();bRet = Shell_NotifyIcon(NIM_DELETE, &tnd);return bRet;void CChatRoomDlg:OnMenuTrayinco()TrayMyIcon();LRESULT CChatRoomDlg:OnTrayCallBackMsg(WPARAM wparam, LPARAM lparam)switch(lparam)case WM_RBUTTONUP:CMenu mMenu, *pMenu = NULL;CPoint pt;mMenu.LoadMenu(IDR_MENU2);pMenu = mMenu.GetSubMenu(0);GetCursorPos(&pt);SetForegroundWindow();pMenu-TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);break;case WM_LBUTTONDBLCLK:ShowWindow(SW_RESTORE);SetForegroundWindow();TrayMyIcon(FALSE);break; 3. 附录:课程设计中要用到的Winsock函数WSAStartup初始化socket库WSACleanup结束socket库的使用socket为所要进行的网络通信建立标识符connect连接到远程主机closesocket结束通信,关闭标识符bind将IP地址、TCP端口号与套接字标识符绑定listen将接受套接字置于被动模式,将服务器置于侦听状态,并指定允许的连接数accept接受下一个呼入的连接recv接收传入的TCP数据recvfrom接收传入的UDP数据select在指定的套接字集准备好接收数据前一直等待send发送TCP数据sendto发送UDP数据shutdown释放TCP连接getpeername从套接字中获取对等方的端口地址setsocketopt获取当前套接字的可变选项gethostbynameinet_addr把域名转换成网络IP地址把用点分十进制表示的IP地址转换成网络IP地址getservbynamegetprotobyname获得服务器的端口号把TCP、UDP转换成相应的服务号码(interger)六、 调试过程测试的目的是为了发现功能是否达到,或者是否有更多的缺陷。当两个客户端在建立连接时,出现过程序假死异常。分析代码发现,线程调度出错,接收消息线程th还未创建和启动,监听线程wait已经被销毁了。修改代码,给出两种解决办法:(1)在th线程的执行方法中,将wait线程销毁;在th线程销毁前,重新开启wait线程。(2)wait线程不销毁。设置一bool类型变量isconnected,当已经建立连接后,isconnected=true,若再有其他的连接请求到达,自动将其他连接请求拒绝。连接断开后,重置isconnected=false,将处理移交给用户。我采用了第一种方法,调试之后,问题解决。在程序退出时,常常发生错误,异常退出,造成表面上看程序已结束,但却仍然驻留在内存中的现象。经过反复分析代码,最终确定问题出在程序退出处理流程上,通过修改和调试,问题解决。软件图示七、 实验结果分析整个开发过程中,共历经多次相互断开、连接测试,聊天模块终于可以正常运行。借助在文本聊天模块开发过程中积累的经验,较顺利的完成了文件传输模块的开发。通过在总集成后的程序的各个流程中添加消息提示框显示程序内部数据、对象的状态,并插入断点进行单步跟踪发现各模块工作正常,数据也未发生异常现象。(1)程序启动之后就能看到当前哪些机器在线,哪些可以与之进行对等通信。(2)一旦有某个网内的机器上线了,要有即时通知,并能及时更新用户界面中的用户列表。(3)当双击某个列表项的时候,要弹出聊天对话框,可以在其中编辑要发送的聊天信息,并进行发送。(4)聊天界面要人性化,下面是发送框,上面有已有聊天记录,并借助滚动条看到当次所有的聊天记录。(5)当有远程用户向本机发送文件的时候,要弹出一个消息提示框,提示本机用户,可以选择接收或者拒绝。(6)当用户觉得有必要保存聊天记录时,提供一个简单的聊天记录保存功能。(7)文件传输过程中,应该有当前传输状态提示,并能够对传输耗时等进行计算,作为当前网络状态的一种反馈。经检查,当用户输入无效操作时,系统总是能检测到无效操作,对用户进行相应的信息反馈,并合理的做出内部处理。当系统本身发生错误,比如网络初始化失败,绑定套接字失败,数据读取失败等,都会进行相对应的正确处理。八、 体会和建议通过这些时间的开发设计,开发基于Socket的聊天系统已经可以按照预定要求实现功能,完全实现了聊天、文件传输等功能。随着科技的发展和社会的进步,聊天系统将需要得到进一步改善,相应的功能模块也应相应的增加,比如多媒体方面的内容,以满足用户需求。通过此次的论文,我学到了很多知识,跨越了传统方式下的教与学的体制束缚,在论文的写作过程中,通过查资料和搜集有关的文献,培养了自学能力和动手能力。并且由原先的被动的接受知识转换为主动的寻求知识,这可以说是学习方法上的一个很大的突破。在以往的传统的学习模式下,我们可能会记住很多的书本知识,但是通过毕业论文,我们学会了如何将学到的知识转化为自己的东西,学会了怎么更好的处理知识和实践相结合的问题。在论文的写作过程中也学到了做任何事情所要有的态度和心态,首先做学问要一丝不苟,对于发展过程中出现的任何问题和偏差都不要轻视,要通过正确的途径去解决,在做事情的过程中要有耐心和毅力,不要一遇到困难就达退堂鼓,只要坚持下去就可以找到思路去解决问题的。而且要学会与人合作,这样做起事情来就可以事倍功半。 总之,此次课设的写作过程,我收获了很多,即为将来的人生之路做好了一个很好的铺垫。 九、 参考文献1 Karli Watson,Christian Nagel等.C入门经典(第3版)M.北京:清华大学出版社.2004.2 宋振会.SQL Server 2000中文版基础教程M.北京:清华大学出版社.2005.3 Karli Watson等.Beginning Visual C# 2005M.北京:清华大学出版.社.2005.4 Jim Arlow Ila Neustadt.UML和统一过程M.北京:机械工业出版社.2003.2.5 佩里.C#和.NET核心技术M.北京:机械工业出版社.2006.7.6 天极网新技术研究室.ASP.NET完全入门M.北京:重庆出版社.2001.7 Scott Worley.ASP.NET技术内幕M.北京:人民邮电出版社.2002.
展开阅读全文