P2P聊天工具
问题描述-1 P2P技术概要 点对点传输技术; 无需经过服务器; P2P更加符合分布式计算的理念; BT下载工具是P2P的典型应用之一;
问题描述-2 界面和和实现的功能说明
问题分析 P2P聊天工具 远程机功能 WHAT:侦听 WHY: 接收客户程序的请求 HOW: TcpListener.Start WHEN: Form Load时 本机功能 WHAT:连接 WHY: 检测连接是否顺利 HOW: TcpClient.Connect WHEN: 用户触发 本机功能 WHAT: 发送消息 SERVER:侦听到,记录 CLIENT: 记录 聊天信息列表
难点解析-1 如何实现侦听 单独开辟一个线程进行侦听,以避免操作阻塞 侦听的方法运行在线程上; 使用TcpListener.Start; 循环、持续检测输入缓冲的是否有信息,读到后解码; 利用这些方法,具体讲解如何从问题描述中抽取对象,以及对象的特征和动作,设计对应类的属性和方法。可参考学生用书问题分析部分,进行讲解。
难点解析-1 参考代码: Ls= new TcpListener (1010); Ls.Start(); SocketOfLs = Ls.AcceptSocket(); while (true) { Byte[] streamofaccept = new Byte[80] ; int i = SocketOfLs.Receive(streamofaccept); MessageOfAccept = System.Text.Encoding.UTF8.GetString (streamofaccept); } 利用这些方法,具体讲解如何从问题描述中抽取对象,以及对象的特征和动作,设计对应类的属性和方法。可参考学生用书问题分析部分,进行讲解。
难点解析-2 如何把类中侦听到的字符随时传递给窗体控件 在类中定义委托和事件; 侦听到消息后,触发事件; 在窗体中的Form_Load方法中创建对象、订购事件; 在事件被触发时,读取类中的字符信息,写到列表 框中; 利用这些方法,具体讲解如何从问题描述中抽取对象,以及对象的特征和动作,设计对应类的属性和方法。可参考学生用书问题分析部分,进行讲解。
难点解析-2 参考代码: //定义事件 public delegate void DelgMsgListened(); public event DelgMsgListened OnMsgListened; //事件在侦听到字符消息时候被触发 OnMsgListened(); //在窗体中预定事件 MyP2P = new P2P(); MyP2P.OnMsgListened+=new P2P.DelgMsgListened(MyP2P_OnMsgListened); 利用这些方法,具体讲解如何从问题描述中抽取对象,以及对象的特征和动作,设计对应类的属性和方法。可参考学生用书问题分析部分,进行讲解。
难点解析-3 如何发送数据 获得传送数据的数据流; 在数据流上定义StreamWriter对象; reqStreamW.Flush( ); 利用这些方法,具体讲解如何从问题描述中抽取对象,以及对象的特征和动作,设计对应类的属性和方法。可参考学生用书问题分析部分,进行讲解。
难点解析-3 参考代码: tcpStream = tcpClientPere.GetStream ( ); 建立连接时: 发送消息时: reqStreamW = new StreamWriter(tcpStream); reqStreamW.Write (sMsg); //sMsg是发送字符串 reqStreamW.Flush ( ) ; 利用这些方法,具体讲解如何从问题描述中抽取对象,以及对象的特征和动作,设计对应类的属性和方法。可参考学生用书问题分析部分,进行讲解。
阶段划分 第一阶段(30分钟):设计完成界面 第二阶段(40分钟):实现侦听功能 第三阶段(20分钟):实现连接功能 (教员提供界面所需要的图片,学员完成界面设计) 第二阶段(40分钟):实现侦听功能 (编码实现远程机的侦听,并显示侦听消息的功能) 第三阶段(20分钟):实现连接功能 (编码实现本机与远程机的连接功能) 第四阶段(30分钟):实现发送消息的功能 (编码实现本机向远程机发送字符消息并显示的功能) 第五阶段(10分钟):运行和调试 (释放资源) (为程序增加退出的功能) (运行程序,完善代码)
第一阶段 第一阶段(30分钟):设计完成界面 要求学员自己动手编码,在编码的过程中解答学员提出 的问题 窗体界面无边框,显示在屏幕中间; 连接、断开、退出、发送消息以及登录,使用的都是图片 来代替按钮功能; 中间的齿轮图片是登录按钮,左下的圆圈是发送消息按钮; 中间的大块区域使用ListBox控件显示聊天信息列表,下方 的多行文本框可以输入要发送的聊天信息; 聊天消息使用ListBox来显示; 要求学员自己动手编码,在编码的过程中解答学员提出 的问题
阶段检查 针对第一阶段检查学员的阶段结果 教员进行辅导
第二阶段 第二阶段(40分钟):实现侦听功能 要求学员自己动手编码,在编码的过程中解答学员提出的 问题 定义类、导入必要的名称空间; 定义委托和事件; 在类中编写方法,实现侦听,并在侦听到消息时触发事件; 编写方法,创建线程,在线程上运行侦听的方法; 在Form_Load中: 创建类的对象->预订对象事件->调用线程方法进行侦听; 在窗体中进行类事件的编码,把侦听到的消息显示在列表框; 要求学员自己动手编码,在编码的过程中解答学员提出的 问题
阶段检查 针对第二阶段检查学员的阶段结果 教员答疑和点评
第二阶段标准代码演示 //窗体中的代码 //类中的名称空间 using System; //在窗体中定义类的对象 private P2PSvr.P2P MyP2P; //Form_Load事件 private void TCPClient_Load(object sender, System.EventArgs e) { MyP2P = new P2PSvr.P2P( ); MyP2P.OnMsgListened+=new P2PSvr.P2P.DelgMsgListened(MyP2P_OnMsgListened); MyP2P.ThreadToListen( ); } //对象的事件 private void MyP2P_OnMsgListened( ) //MsgLisened是对象的属性,返回侦听到的消息 lstMsg.Items.Add(MyP2P.MsgLisened); //类中的名称空间 using System; using System.Net.Sockets; using System.IO; using System.Threading ; public class P2P { //本阶段中使用到的类中的成员变量 //委托和事件,在侦听到消息的时候通知前端 public delegate void DelgMsgListened( ); public event DelgMsgListened OnMsgListened; //侦听到的消息 private string m_msgLisened; //远程机是否正常连接 private bool isConnected=false; //创建线程,用以侦听端口号,接收信息 private Thread lsThread; //用以侦听端口号 private TcpListener lsClient; //设定标识位,判断侦听状态 private bool isListenerNow = true; public void BeginLisen( ) { try lsClient= new TcpListener (8877); lsClient.Start( ); //接受远程计算机的连接请求,并获得用以接收数据的Socket实例 skSocket = lsClient.AcceptSocket(); //循环侦听 while (isListenerNow) Byte[ ] stream = new Byte[80] ; int i = skSocket.Receive(stream); m_msgLisened = System.Text.Encoding.UTF8.GetString (stream); isConnected=true; OnMsgListened(); } catch (System.Security.SecurityException ) isConnected=false; public void ThreadToListen( ) lsThread = new Thread (new ThreadStart(this.BeginLisen)); lsThread.Start( );
第三阶段 第三阶段(20分钟):实现连接功能 要求学员自己动手编码,在编码的过程中解答学员提出的 问题 定义TCPClient对象; 在类中编写连接的方法; 在窗体的”连接”按钮事件中调用方法; 要求学员自己动手编码,在编码的过程中解答学员提出的 问题
阶段检查 针对第三阶段检查学员的阶段结果 教员答疑和点评
第三阶段标准代码演示 //类中的方法 public bool ConnectToRemote(string svrName) { try { tcpClientPere = new TcpClient() ; //向远程计算机提出连接申请 tcpClientPere.Connect(svrName,8877); //如果连接申请建立,获得传送数据的数据流 tcpStream = tcpClientPere.GetStream(); return true; } catch (Exception) { return false; //窗体调用… private void btnConnectToServer_Click(object sender, System.EventArgs e) if (MyP2P.ConnectToRemote(txtServerName.Text.Trim())) { btnSend.Visible = true; panLogin.Visible = false; this.Refresh(); else { txtSend.Text = "目标计算机拒绝连接请求......" ;
第四阶段 第四阶段(30分钟):实现发送消息的功能 要求学员自己动手编码,在编码的过程中解答学员提出的 问题 在类中编写发送消息的方法; 在窗体中调用方法,并显示发送的消息到列表框; 要求学员自己动手编码,在编码的过程中解答学员提出的 问题
阶段检查 针对第四阶段检查学员的阶段结果 教员答疑和点评
第四阶段标准代码演示 //类中的方法 public bool SendMsg(string msgToSend) { try { reqStreamW = new StreamWriter(tcpStream); reqStreamW.Write (msgToSend); //清理当前编写器的所有缓冲区,并使所有缓冲数据写入流 reqStreamW.Flush ( ) ; return true; } catch (Exception) { return false; //窗体调用 private void btnSend_Click(object sender, System.EventArgs e) if (txtSend.Text.Length ==0) {return;} if ((txtSend.Text).Length>40) {return;} string sMsg = txtUserName.Text +": "+txtSend.Text; if (MyP2P.SendMsg(sMsg)) { lstMsg.Items.Add(sMsg) ; txtSend.Clear(); else { txtSend.Text = "无法发送消息到目标计算机!"; 定义了两个public常量用来指代情绪值。 访问成员变量:不论一个对象的数据隐藏得多么好,客户仍然需要访问一些隐藏的数据,这是通过调用函数或方法来实现的。在Java中,可以使用特殊的被称做属性访问器的方法来访问隐藏的数据。在Java中属性访问器和通常的函数之间并没有本质的区别。将一个通常的方法转变成一个属性访问器唯一要做的事情就是参照一个命名规则来添加方法。 可以使用多态机制让完成相似功能的不同的方法拥有相同的名字但是拥有不同的参数列表。
第五阶段 第五阶段(10分钟):运行和调试 要求学员自己动手编码,在编码的过程中解答学员提出 的问题 释放资源; 实现退出的功能; 完善代码; 要求学员自己动手编码,在编码的过程中解答学员提出 的问题
阶段检查 抽查全部完成的学员,并上台演示 教员在VS.NET中演示全部的程序
扩展部分 扩展部分1 编码实现窗体的拖动; 扩展部分2 编码实现连接双方进行文件发送和接收的功能;
总结 通过本次项目案例,练习了C#程序设计中的多线程、IO流;WinForms高级编程中的界面设计、P2P技术 巩固的知识点 基本的窗体和控件的使用; 委托和事件; IO流; 线程的创建和启动; TcpLisener、TcpClient对象;