基于套接字的聊天程序设计_第1页
基于套接字的聊天程序设计_第2页
基于套接字的聊天程序设计_第3页
基于套接字的聊天程序设计_第4页
基于套接字的聊天程序设计_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

TCP/IP课程设计题目: 基于套接字的聊天程序设计院 系:计算机与电子信息学院专 业:网络工程班 级:网络10-2学 号:10034120228姓 名:刘亮日 期:2012.9.25二O一二年编制1课程设计任务书一、设计题目基于TCP和UDP套接字的聊天程序设计二、设计目的:(1) 理解客户机/服务器模型的工作原理;(2) 掌握套接字的概念;(3) 掌握基于套接字的面向连接和无连接客户机/服务器程序的设计原理和相关的WinSock API函数;(4) 掌握基于TCP和UDP的程序设计方法。三、设计内容:(一) 基于TCP的应用编程:编写一个Client/Server程序对,服务器程序负责接收客户机进程的连接请求,并在服务器进程与客户机进程之间建立通信连接,然后接收客户机进程的传送数据并将其显示在服务器端,同时将欢迎信息发送给客户机进程,通信结束后关闭该客户机进程的连接;客户机进程首先提出对指定服务器的连接请求,建立连接后向服务器进程发送已经建立连接的数据信息,同时接收服务器进程发送过来的数据并显示在客户端。采用WinSock AIP实现代码。(二) 基于UDP的应用编程:实现一个基于UDP的客户机/服务器程序,通过设定不同的命令行参数来确定应用进程的不同角色,即充当客户机进程还是服务器进程。当两个进程运行后,彼此之间可以轮流发送消息,对方接收后显示出来。四、开发环境和语言:(1)网络:局域网;(2)开发环境:VS2005以上或VC+6.0以上版本;(3)开放语言:C/C+。目录1 通信原理分析- 3 -1.1 TCP协议- 3 -1.3 套接字的定义- 4 -1.4 客户机/服务器模式的运行原理- 5 -2 系统设计- 5 -2.1 基于TCP应用程序设计:- 5 -2.2 基于UDP应用程序设计:- 5 -3 详细设计- 6 -3.1基于TCP的应用程序设计:- 6 -3.1.1 服务器设计:- 6 -3.1.2 客户端设计:- 9 -3.2基于UDP的应用程序设计:- 12 -3.2.1 服务端设计:- 12 -3.2.2 客户端设计:- 30 -4 代码调试与运行结果- 34 -4.1基于TCP的应用程序调试结果:- 34 -4.2基于UDP的运用程序调试结果:- 35 -5 总结- 38 -5.1 设计不足- 38 -5.2 体会感想- 39 -1 通信原理分析1.1 TCP协议 TCP协议是一种面向连接的、可靠的传输层协议,为应用层提供可靠、全双工的数据流传输服务,TCP报文封装在IP数据报中。在使用TCP进行通信时需要首先建立TCP连接后再能进行数据的传输,TCP连接建立为在不安全的IP网络中传输数据提供了数据完整的保障,在通信结束后要将该连接断开。 下面给出基于TCP的c/s通信工作模式: 服务器进程 客户端进程socket()建立流套接字bind()套接字与本地地址绑定listen()侦听,准备接受连接accept()接受连接close()关闭套接字等待客户端进程连接请求的的到来recv()接受数据send()发送数据socket()建立流套接字connection()将套接字与远程主机连接send()发送数据recv()接受数据close()关闭套接字交换数据交换数据基于tcp的c/s通信模式1.2 UDP协议UDP是一种无连接、尽最大努力交付的运输层协议,不提供流量控制和确认机制,数据报可能丢失、延迟、乱序到达。这就是的UDP在实现起来就较为简单,这种简单能很好的应用在实时通信服务中,如实时语音传输、实时视频通信等。 下面给出基于UDP的c/s通信工作模式: 进程A 进程Bclose()关闭套接字定socket()建立套接字bind()套接字与本地地址绑定recvform()接受数据socket()建立套接字bind()套接字与本地地址绑定recvform()接受数据sendto()发送数据close()关闭套接字sendto()发送数据交换数据交换数据基于udpP的c/s通信模式1.3 套接字的定义 套接字我们可以认为就是通信的一端,其主要包括协议、ip、端口号。将两个套接字连接在一起就可以实现端到端的网络通信了。在套接字中封装了网络和传输层协议,从而为程序员屏蔽了底层通信的复杂性。 针对不同通信的需求,在TCP/IP协议中提供了3种不同套接字类型,分别为: 流式套接字(SOCK_STREAM)、数据保式套接字(SOCK_DGRAM)、原始套接字(SOCK_RAW)。流式套接字(SOCK_STREAM) 提供面向连接的,可靠的数据传输服务,数据无差错,无重复的发送,且按发送的顺序接收,基于 TCP 协议。数据保式套接字(SOCK_DGRAM) 提供无连接的服务,数据包以独立包形式发送,不提供无错误的保证,数据可能丢失或重复,且接收顺序混乱,基于 UDP 协议。原始套接字(SOCK_RAW) 主要是在编写自定义底层协议的应用程序时使用。 1.4 客户机/服务器模式的运行原理 网络应用程序一般是以客户机/服务器的模型的方式工作的。在这种工作方式中,一个服务器程序通常事先启动, 并在一个熟知端口侦听对服务器的请求。当客户机应用程序需要某种服务时,需向提供这种服务的服务器发出请求,服务器在接收到请求后,向客户机发出相应请求信息。这样 客户机应用程序和服务器程序之间便建立了通信连接, 此后可以进行数据通信。通信任务完成后需要关闭它们之间的通信连接。 2 系统设计2.1 基于TCP应用程序设计:发送/接受数据 服务器进程服务器进程 客户机进程发送/接受数据2.2 基于UDP应用程序设计:服务器进程登陆,注册信息到服务器请求客户A的ip和端口信息登陆,注册信息到服务器请求客户B的ip和端口信息客户机B进程客户机A进程3 详细设计3.1基于TCP的应用程序设计:3.1.1 服务器设计:#include #include using namespace std;#include int main(int argc, char *argv)const int DEFAULT_PORT = 5000; WORD wVersionRequested;WSADATA wsaData;int err,iLen;wVersionRequested=MAKEWORD( 2, 2 );err = WSAStartup( wVersionRequested, &wsaData );if ( err != 0 ) cout加载WinSock失败!;return 0;/创建用于监听的套接字SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0); /创建服务器监听套接字if (sockSrv = INVALID_SOCKET)coutsocket() fail:WSAGetLastError()endl;return 0;SOCKADDR_IN addrSrv;char cip20=;int port;char type;cout 服务器基本设置为 ip: 端口:5000endl;cout 是否从新设置:Y or Ntype;if(type=Y) coutcip; /ip=cip;coutport;addrSrv.sin_family = AF_INET;addrSrv.sin_addr.S_un.S_addr = inet_addr(cip);addrSrv.sin_port = htons(port); elseaddrSrv.sin_family = AF_INET;addrSrv.sin_addr.S_un.S_addr = inet_addr();addrSrv.sin_port = htons(DEFAULT_PORT); /绑定本地主机IP和端口err = bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR);if ( err != 0 ) coutbind() fail:WSAGetLastError()endl;return 0;coutServer has been started successfully.endl;/监听err = listen(sockSrv,5);if ( err != 0 ) coutlisten() fail:WSAGetLastError()endl;return 0;coutServer waiting:.endl;SOCKADDR_IN addrClient;int len = sizeof(SOCKADDR);while(1)SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);/接收客户进程连接请求if (sockConn = INVALID_SOCKET)coutaccept() fail:WSAGetLastError()endl;break;char recvBuf1024 = 0;iLen = recv(sockConn,recvBuf,1024,0);/接收数据if (iLen = SOCKET_ERROR)coutrecv() fail:WSAGetLastError()endl;break;recvBufiLen = 0;coutrecvBufendl;char sendBuf1024,hostname100;if (gethostname(hostname,100) != 0) /获取主机名称strcpy(hostname,None);sprintf(sendBuf,welcome %s connected to %s!,inet_ntoa(addrClient.sin_addr),hostname);err = send(sockConn,sendBuf,strlen(sendBuf)+1,0); /发送数据if (err = SOCKET_ERROR)coutsend() fail:WSAGetLastError()endl;break; while(1 ) char recvBuf1024 = 0;iLen = recv(sockConn,recvBuf,1024,0);/接收数据if (iLen = SOCKET_ERROR)coutrecv() fail:WSAGetLastError()endl;break;recvBufiLen = 0;coutrecvBufendl;cout请输入你要发送的消息,输入E退出sendBuf;string str=sendBuf;if(pare(E)err = send(sockConn,sendBuf,strlen(sendBuf)+1,0); /发送数据if (err = SOCKET_ERROR)coutsend() fail:WSAGetLastError()endl;break; elsebreak; closesocket(sockConn); /关闭套接字coutServer waiting:.endl; WSACleanup(); return 0; 3.1.2 客户端设计:#include #include using namespace std;#include int main(int argc, char *argv)const int DEFAULT_PORT = 5000; WORD wVersionRequested;WSADATA wsaData;int err,iLen;wVersionRequested=MAKEWORD( 2, 2 );err = WSAStartup( wVersionRequested, &wsaData );if ( err != 0 ) cout加载WinSock失败!;return 0;/创建用于连接的套接字SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);if (sockClient = INVALID_SOCKET)coutsocket() fail:WSAGetLastError()endl;return 0;SOCKADDR_IN addrSrv;/string ip;char cip20=;int port;char type;cout 服务器基本设置为 ip: 端口:5000endl;cout 是否从新设置:Y or Ntype;if(type=Y) coutcip; /ip=cip;coutport;addrSrv.sin_family = AF_INET;addrSrv.sin_addr.S_un.S_addr = inet_addr(cip);addrSrv.sin_port = htons(port); elseaddrSrv.sin_family = AF_INET;addrSrv.sin_addr.S_un.S_addr = inet_addr();addrSrv.sin_port = htons(DEFAULT_PORT); err = connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR);/请求连接服务器进程if ( err = INVALID_SOCKET ) coutconnect() fail:WSAGetLastError()endl;return 0;char sendBuf1024,hostname100;if (gethostname(hostname,100) != 0) /获取主机名称strcpy(hostname,None);strcpy(sendBuf,hostname);strcat(sendBuf, have conneted to you!);err = send(sockClient,sendBuf,strlen(sendBuf)+1,0); /发送数据if (err = SOCKET_ERROR)coutsend() fail:WSAGetLastError()endl;return 0;char recvBuf1024;iLen = recv(sockClient,recvBuf,1024,0); /接收数据if (iLen = 0)return 0;else if (iLen = SOCKET_ERROR)coutrecv() fail:WSAGetLastError()endl;return 0;elserecvBufiLen = 0;coutrecvBufendl;while(1)char sendBuf1024; cout请输入你要发送的消息,输入E退出sendBuf;string str=sendBuf;if(pare(E) err = send(sockClient,sendBuf,strlen(sendBuf)+1,0); /发送数据if (err = SOCKET_ERROR)coutsend() fail:WSAGetLastError()endl;return 0;elsebreak;char recvBuf1024;iLen = recv(sockClient,recvBuf,1024,0); /接收数据if (iLen = 0)return 0;else if (iLen = SOCKET_ERROR)coutrecv() fail:WSAGetLastError()endl;return 0;elserecvBufiLen = 0;coutrecvBufOnSend(nErrorCode);/事件响应函数(当一个套接字准备好了,并且可以利用Receive接收时,就可以去调用该函数去处理相应的消息)void CUDPSocket:OnReceive(int nErrorCode)CCUDPServerDlg *pDl = CCUDPServerDlg:GetDialog();pDl-OnReceive(nErrorCode);/地址和端口绑定bool CUDPSocket:BindLocatePC(DWORD m_LocateIP,int m_LocatePort)in_addr tempAddr;CString buf;/*LPCTSTR类型 L表示long指针 这是为了兼容Windows 3.1等16位操作系统遗留下来的,在win32中以及其他的32为操作系统中, long指针和near指针及far修饰符都是为了兼容的作用。没有实际意义。 P表示这是一个指针;C表示是一个常量;T表示在Win32环境中,有一个_T宏,这个宏用来表示你的字符是否使用UNICODE, 如果你的程序定义了UNICODE或者其他相关的宏,那么这个字符或者字符串将被作为UNICODE字符串,否则就是标准的ANSI字符串。 STR表示这个变量是一个字符串所以LPCTSTR就表示一个指向常固定地址的可以根据一些宏定义改变语义的字符串。同样, LPCSTR就只能是一个ANSI字符串,在程序中我们大部分时间要使用带T的类型定义。LPCTSTR = const TCHAR *=const char * LPCSTR与char的区别 在美国国家标准下,LPCTSTR为const char *,是常量,不可修改;而char,是变量,可以修改。两者的存储位置是不同的,如果强制转换的话,不安全。 */LPCTSTR p ; /获取本地IPtempAddr.S_un.S_addr = htonl(m_LocateIP);buf = inet_ntoa(tempAddr);/将网络字节的顺序的表示的IP地址转换为点分十进制表示的IP地址p = (LPCTSTR) buf;/绑定本地IP /* Create函数*/if (!Create(htons(m_LocatePort),SOCK_DGRAM,FD_READ|FD_WRITE,p) /htons函数是将16位的端口号从主机顺序装换为网络字节顺序,SOCK_DGRAM表示数据报套接字return false; /设置连接标志m_fConnected = true;return true ;/向远程主机中发送数bool CUDPSocket:SendToRemotePC(DWORD m_RemoteIP,int m_RemotePort,CString m_SendData)in_addr tempAddr;char send_buf1024 ;CString buf;LPCTSTR p ;/发送数据if (m_fConnected) /判断当前已经为CAsyncSocket对象创建了套接字/获取远程IPtempAddr.S_un.S_addr = htonl(m_RemoteIP); /htonl函数是将4字节的IP地址从主机顺序装换为网络字节顺序buf = inet_ntoa(tempAddr);/inet_ntoa将网络字节顺序表示的IP地址装换位点分十进制表示的IP地址p = (LPCTSTR) buf;strcpy(send_buf,(char *)m_SendData.GetBuffer(m_SendData.GetLength(); /获取数据/发送数据 参数说明:代发送数据的数据缓存区,待发送数据的长度,远程套接字的结构地址,远程主机套接字结构地址的长度,发送数据的方式/执行成功时,就会饭后发送数据的个数(以字符作为单位)int iByte = SendTo(send_buf,strlen(send_buf)+1,htons(m_RemotePort),p);/htons函数是将16为的端口号从主机顺序装换为网络字节顺序/判断是否发送成功if (iByte0) return true ;elsereturn false ;return false;Cuser类,该类主要保存用户的基本信息下面给出该类的源代码/user.h : 头文件#pragma onceclass Cuserpublic:CString IP; /用户ip地址int PORT;/用户开设的端口CString userName; /用户姓名CString ID; /用户标识,使用ip+Port来进行标识Cuser(CString ip,int port,CString name,CString id); Cuser(void);Cuser(void);void setUserName(CString name); /设置用户姓名 CString getUserName( ); /返回用户姓名void setIP(CString ip); /设置IP CString getIP( ); /返回IPvoid setPORT(int port); /设置端口 int getPORT( ); /返回端口void setID(CString id); /设置ID CString getID( ); /返回ID;/user.cpp#include StdAfx.h#include user.h/重写构造函数Cuser:Cuser(CString ip,int port,CString name,CString id)this-IP=ip; /初始化ipthis-PORT=port;/初始化端口this-userName=name;this-ID=id;/默认构造函数Cuser:Cuser(void)Cuser:Cuser(void)void Cuser:setUserName(CString name) /设置用户姓名this-userName=name;CString Cuser:getUserName( ) /返回用户姓名return this-userName;void Cuser:setIP(CString ip) /设置IPthis-IP=ip;CString Cuser:getIP( ) /返回IPreturn this-IP;void Cuser:setPORT(int port) /设置端口this-PORT=port;int Cuser:getPORT( ) /返回端口return this-PORT;void Cuser:setID(CString id) /设置IDthis-ID=id;CString Cuser:getID( )/返回IDreturn this-ID;CCUDPServerDlg类,该类为界面类,主要进行与用户进行交互下面给出该类的源代码/ CUDPServerDlg.h : 头文件/#pragma once#includeUDPSocket.h#include user.hconst int userMaxNum = 50; /定义最大用户数量/ CCUDPServerDlg 对话框class CCUDPServerDlg : public CDialogEx/ 构造public:CCUDPServerDlg(CWnd* pParent = NULL);/ 标准构造函数/ 对话框数据/enum IDD=IDD_CUDPSERVER_DIALOG ;enum IDD = IDD_CUDPSERVER_DIA;protected:virtual void DoDataExchange(CDataExchange* pDX);/ DDX/DDV 支持/ 实现protected:HICON m_hIcon;/ 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()public:afx_msg void OnBnClickedBtnStart();afx_msg void OnBnClickedBtnCancle();static CCUDPServerDlg *GetDialog();void OnSend(int nErrorCode); /发送数据 void OnReceive(int nErrorCode);/接受数据int numUser;/表示当前用户数量Cuser userArrayuserMaxNum; /定义用户数组CUDPSocket*m_pSocket; bool sameUser(Cuser user); /判断用户是已经存在void removeUser(CString id); /将退出的用户从堆栈中移除void UpdateUserInfo(); /更新当前用户用户void send(CString type,CString& strData); /发送数据DWORD m_LocateIP; / 本地绑定IPint m_LocatePort; / 地绑定端口号CString m_ReceiveData; / 接收数据使用void Split_CString( CString& source,CStringArray& dest, const CString& division );/字符串分割使用的CString findUser( CString& name); /根据用户名查找该用户的ip和端口信息;/CUDPServerDlg.cpp/ CUDPServerDlg.cpp : 实现文件/#include stdafx.h#include CUDPServer.h#include CUDPServerDlg.h#include afxdialogex.h#ifdef _DEBUG#define new DEBUG_NEW#endifconst int DATA_BUFFER_LENGHT = 1024; /定义数据块的大小/ 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogExpublic:CAboutDlg();/ 对话框数据enum IDD = IDD_ABOUTBOX ;protected:virtual void DoDataExchange(CDataExchange* pDX); / DDX/DDV 支持/ 实现protected:DECLARE_MESSAGE_MAP();CAboutDlg:CAboutDlg() : CDialogEx(CAboutDlg:IDD)void CAboutDlg:DoDataExchange(CDataExchange* pDX)CDialogEx:DoDataExchange(pDX);BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)END_MESSAGE_MAP()/ CCUDPServerDlg 对话框CCUDPServerDlg:CCUDPServerDlg(CWnd* pParent /*=NULL*/): CDialogEx(CCUDPServerDlg:IDD, pParent), m_LocateIP(0), m_LocatePort(0), m_ReceiveData(_T()m_hIcon = AfxGetApp()-LoadIcon(IDR_MAINFRAME);m_LocateIP = ntohl(inet_addr(); /初始化本地IPm_LocatePort=5900;this-numUser=0; /初始化用户数量void CCUDPServerDlg:DoDataExchange(CDataExchange* pDX)CDialogEx:DoDataExchange(pDX);DDX_IPAddress(pDX, IDC_IPADDRESS, m_LocateIP);DDX_Text(pDX, IDC_Port, m_LocatePort);DDX_Text(pDX, IDC_Info, m_ReceiveData);BEGIN_MESSAGE_MAP(CCUDPServerDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_Btn_Start, &CCUDPServerDlg:OnBnClickedBtnStart)ON_BN_CLICKED(IDC_Btn_Cancle, &CCUDPServerDlg:OnBnClickedBtnCancle)END_MESSAGE_MAP()/ CCUDPServerDlg 消息处理程序BOOL CCUDPServerDlg:OnInitDialog()CDialogEx:OnInitDialog();/ 将“关于.”菜单项添加到系统菜单中。/ IDM_ABOUTBOX 必须在系统命令范围内。ASSERT(IDM_ABOUTBOX & 0xFFF0) = IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX AppendMenu(MF_SEPARATOR);pSysMenu-AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);/ 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动/ 执行此操作SetIcon(m_hIcon, TRUE);/ 设置大图标SetIcon(m_hIcon, FALSE);/ 设置小图标/ TODO: 在此添加额外的初始化代码return TRUE; / 除非将焦点设置到控件,否则返回 TRUEvoid CCUDPServerDlg:OnSysCommand(UINT nID, LPARAM lParam)if (nID & 0xFFF0) = IDM_ABOUTBOX)CAboutDlg dlgAbout;dlgAbout.DoModal();elseCDialogEx:OnSysCommand(nID, lParam);/ 如果向对话框添加最小化按钮,则需要下面的代码/ 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,/ 这将由框架自动完成。void CCUDPServerDlg:OnPaint()if (IsIconic()CPaintDC dc(this); / 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc(), 0);/ 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;/ 绘制图标dc.DrawIcon(x, y, m_hIcon);elseCDialogEx:OnPaint();/当用户拖动最小化窗口时系统调用此函数取得光标/显示。HCURSOR CCUDPServerDlg:OnQueryDragIcon()return static_cast(m_hIcon);/退出返回void CCUDPServerDlg:OnB

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论