C#利用Socket实现客户端之间直接通信

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

实验功能:

  1.  设计程序,分别构建通信的两端:服务器端和客户端应用程序,套接字类型为面向连接的Socket,自己构建双方的应答模式,实现双方的数据的发送和接收(S发给CC发给S)。

  2. 服务端程序能响应单个或任意多个客户端连接请求;服务端能向单个客户发送消息,支持群发消息给所有客户端;

  3. 通信的双方具备异常响应功能,包括对方异常退出的处理。如果客户端退出,服务器有响应;反之亦然。

  4. 客户端之间直接通信,CC之间直接通信(不是通过S传递)。

设计思路:


  1. 服务器设计思路:服务器的设计是这次实验最复杂的部分,因为服务器的功能比较多。作为服务器,它要可以同时与多个客户端连接,为每一个连接的客户端创建一个通信Socket,自己还要有一个Socket用于监听客户端的连接请求;服务器要创建一个数据结构用于保存连接进来的客户端的信息(Socket和客户端的名字);服务器要将连接进来的客户端显示出来,用户可以根据显示出来的用户列表来向指定的客户端发信息;服务器要能及时地刷新客户端列表,当有新的客户端连接进来或是退出的时候要及时通知所有的客户端并刷新自己的客户端列表;服务器要能接收所有的客户端的信息,并将信息无错地转发给指定的客户端

  2. 客户端设计思路:客户端的设计相对于服务器来说的话对会比较简单一点。客户端要有接收服务器信息的功能,但客户端只向服务器发信息,客户端通过服务器的转发功能向其它的客户端发送信息。客户端要可以处理服务器发过来的信息,还要有数据结构用来保存所有客户端的名字,并将所有客户端名字列表显示出来。可以指定客户端列表里面的多个项来向不同的客户端发信息。

  3. 通信数据处理:无论是服务器发给客户端,还是客户端发给服务器的数据,双方都要进行处理。对于不用的类型的数据要设计不用的标志信息,当双方收到信息后跟据标志信息进行不同的处理。数据可以分为三种 

    a)登陆信息。这类信息提示有新的客户端连接进来。该信息由客户端首先发给服务器,服务器收到后会更新自己的在线客户端列表,增加与该客户端通信的Socket和名字,并将该信息转发给所有在线的客户端,提醒客户端即时更新客户端列表。这类信息以“login,客户端名”的形式发送。

    b)退出信息。这类信息提示发信息的客户端即将退出服务器。该信息由客户端首先发给服务器,服务器收到后会更新自己的在线客户端列表,删除与该客户端通信的Socket和名字,并将该信息转发给所有在线的客户端,提醒客户端即时更新客户端列表。这类信息以“logout,客户端名”的形式发送。

    c)通信信息。这类信息提示发送信息的客户端向在线的某个客户端或是服务器发起了通信,也可以是服务器与某个客户端发起了通信。如果该信息是服务器发给客户端或是客户端发给服务器,则直接发送,不用经过转发;如果是客户端向另一个客户端发送信息,则是先发给服务器,服务再转发给指定的客户端。这类信息以“talk,目的客户端名,发送的信息”的形式发送。

  4.  线程的设计思路:在服务器方面,需要一个程专门用于监听客户端的连接请求,对于连接进来的每一个客户端,还要创建一个线程用于接收信息,程序的主线程用于向不同的客户端发送信息,所以服务器至少需要要n+2(n>=0)个线程;在客户端方面,需要一个线程用于接收服务的信息,还要一个线程用于向服务器发送信息,所以只需要2个线程

  5. 信息无边界问题:由于这里用的C#里面原始Socket套接字,所以在数据收发的过程中会出现无边界的问题。有时服务器向客户端发送多条不同类型的信息,客户端会把它们合并在一起,当成一条信息处理。为了提取不同类型的信息,发送信息之前要为每一条信息加特定的结束符。

  6. 客户端之间直接通信问题:为了实现客户端之间的直接通信,客户端之间必须知道其它客户端的IP和端口,这可以通过服务器的转发得到客户端之间的IP和端口。客户端也必须有一个自己可用的端口号用来和其它客户端之间的通信,所以除了第一次的客户端与服务器的连接以外,客户端即是服务器也是客户端。

服务器处理不同类型信息代码:

 string[] splitString = receiveString.Split(',');             //分割字符switch (splitString[0].ToLower()){case  "login":                            // 登陆信息user.username = splitString[1];userList.Add (user);                  // 增加用户列表AddItemToListBox (user.username);     // 刷新用户列表sendToAllClient (user,receiveString); // 通知所有在线用户FirstLogin (user);break; case  "logout":                           // 退出信息DeletItemInListBox (user.username);  sendToAllClient (user,receiveString);// 通知所有在线用户 RemoveUser (user);                   // 删除用户信息UserCount (--usercount);             // 刷新用户列表break; case  "talk":                            // 对话信息multMessage (user,receiveString);    // 转发对话break; default: sendMessageTorichBox ("不知道什么意思!");break; }

服务器监听客户端代码:

private  void button1_Click(object sender, EventArgs e){isNormalExit  = false;buttu_richBoxDelegate  d = buttu_richBox;       // 委托事件try {myListener.Listen (10);                          // 开始监听richTextBox1.Invoke(d,"成功监听.");           // 成功监听} catch{richTextBox1.Invoke(d,"监听失败。");         }Thread mhThread = new Thread(ListenClientConnect);  // 创建新的线程mhThread.IsBackground = true;                       // 设置为后台线程mhThread.Start ();button1.Enabled=false;                              // 开始监听按钮不可用button2.Enabled= true;                                }

服务器接受客户端代码:

private void ListenClientConnect ()
{Socket newClient =null;While (isNormalExit==false){
try {newClient = myListener.Accept();          // 接受客户端if(isNormalExit == true)                  // 如果服务器停止监听{ newClient.Close();                     // 关闭Socketusercount = 0;UserCount(usercount);Break;}}Catch{break;}User user = new User(newClient);                 // 保存客户端列表Thread threadReceive = new Thread(ReceiveData);  // 创建新的线程threadReceive.IsBackground=true;                 //设置为后台线程threadReceive.Start(user);                       // 开始线程UserCount(++usercount);                         // 客户端人数加1}}

客户端连接服务器代码:

Private  void button1_Click(object sender, EventArgs e)
{  button1.Enabled = false;client  = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);                              //新建套接字AddrichTextBox1Massage d = sendrichTextBox1Massage;Try {String  name = Dns.GetHostName();                     // 获得计算机的名字IPHostEntry me = Dns.GetHostEntry (name);             //获得计算机IPforeach(IPAddress ips in me.AddressList){Try { IPEndPoint ep = new  IPEndPoint(ips, 8889);  client.Connect(new IPEndPoint(ips, 8889));      // 连接服务器break;}catch{//若获取的IP是vs6的话 
}}client.Send(Encoding.UTF8.GetBytes("login," + textBox1.Text));//向服务器发信息Thread threadReceive = new Thread(new ThreadStart(ReceiveData));//创建新线程threadReceive.IsBackground = true;                           // 设置为后台线程threadReceive.Start();                                       //开始线程}

客户端接受服务器信息代码:

private void ReceiveData(){AddrichTextBox1Massage d = sendrichTextBox1Massage;int receiveLength;while(isExit==false){try{receiveLength = client.Receive(result);             //开始接收信息recieveMessage=Encoding.UTF8.GetString(result,0,receiveLength);}catch{if (isExit == false){richTextBox1.Invoke(d, "与服务器失去联系。"); client.Shutdown(SocketShutdown.Both);            // 关闭套接字client.Close();}break;}string[] splitString = recieveMessage.Split(',');         //处理信息string command = splitString[0].ToLower();switch(command) {case "login":AddOnline(recieveMessage);               // 登陆信息break;case "logout": RemoveUserName(splitString[1]);        // 退出信息break;case "talk": richTextBox1.Invoke(d, "["+splitString[1] + "]对我说: " + splitString[2]);                         // 对话信息break;default: richTextBox1.Invoke(d,"不知什么意思。"); break;} }LostConnect();                                               //关闭连接}

客户端监听其它客户端代码:

private void ServerReceive(Object client)
{AddrichTextBox1Massage d = sendrichTextBox1Massage;Socket myClientSocket = (Socket)client;byte[] str =new byte[1024];while (true) {try{int n = myClientSocket.Receive(str);richTextBox1.Invoke(d, Encoding.UTF8.GetString(str, 0, n));break;}catch {myClientSocket.Close();//richTextBox1.Invoke(d, "接收消息失败!");break;}}myClientSocket.Close();}

程序运行效果:

  1. 服务器运行界面:

    102035_qppN_1540055.png

  2. 有客户端连接进服务器:

    102123_3210_1540055.png

    线客户列表显示了连接进的客户端的名字,在线客户人数显示为3

    上图表示有3个客户端连接进了服务器。

  3. 服务器向客户端发送信息:

    102235_RGri_1540055.png

    服务器向在线客户列表里的2个客户同时发了信息,2个客户端收到了正确的信息。

  4. 客户端的启动界面:

    102408_nMKG_1540055.png

    客户端自动生成用户的名字。

  5. 客户端登陆的界面:

    102505_4j7s_1540055.png

    客户端显示连接成功,并刷新在线用户列表。

  6. 多个客户端连接服务器时的界面:

    102630_n6zz_1540055.png

    当有多个客户端与服务器连接时,客户端会自动更新在线用户列表。

  7. 客户端向其它客户端发TCP信息:

    102721_5V6M_1540055.png

    客户端可以同时向服务器和多个客户端发送信息。

  8. 客户端接收来自其它客户端的TCP信息:

    102903_QHjg_1540055.png

    接收的信息是其它客户端直接发过来的,不经过服务器的转发。

  9. 客户端退出时:

    102944_dfb7_1540055.png

    客户端退出时,服务器会知道退出的用户,并把该客户端移出列表,同时发信息通知其它的客户端,使它们可以及时地更新用户列表。

  10. 服务器退出时:

    103028_RW7D_1540055.png

    当服务器退出时,所有的客户端会提示与服务器失去联系,并将在线用户列表清空。

转载于:https://my.oschina.net/u/1540055/blog/280470

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/291240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在画面中摆放大量图片

2019独角兽企业重金招聘Python工程师标准>>> 有设计经验的一般都知道,版式设计需要对画面元素之间的关系有充分的认识,并能够在足够有限空间内合理布局,将图形与文字合理结合,下面就来给大家介绍下版式设计的方法。 如…

ASP.NET Core 替换 Action 实际执行方法

RequestDelegate上次,我们在《如何判断当前请求的API类型》中查看endpoints.MapControllers()实现时,最终定位到ActionEndpointFactory.cs,其中有这样一段代码:private static RequestDelegate CreateRequestDelegate() {// We do…

最新版富文本编辑器UEditor操作教程

最近项目中使用到了富文本编辑器,选择的是百度的UEditor。所以对其进行了研究,发现最近发布了新版本,与以往的用法有的改变。一下对UEditotr 的是用做一下详细的介绍。 首先是UEditotr的下载,下载地址:http://ueditor.…

Servlet请求转发中文乱码解决

2019独角兽企业重金招聘Python工程师标准>>> 在Servlet的使用过程中,如果在请求转发的同时需要获得输出流并且写出数据的时候,需要设定resp 的编码格式,否则的话,跳转之后的页面很可能会出现中文乱码的问题。 转载于:h…

NodeJs .net core connect Azure service bus

最近有个项目需要使用nodejs 使用语言是Typescript 发送消息给Azure service bus消息格式是XML.但是发送到queue中并不是xml格式,而是string.string格式的消息直到看到azure/service-bus sdk 接口的定义才发现.如果接收消息应用不是javascript sdk框架(因为使用是.net sdk框架接…

c++代码寻找USB00端口并添加打印机

USB00*端口的背景 插入USB端口的打印机,安装打印机驱动,在控制面板设备与打印机处的打印机对象上右击,可以看到打印机端口。对于不少型号,这个端口是USB001或USB002之类的。 经观察,这些USB00*端口并不是打印机驱动所…

IOS调用WCF提供的服务方法,但是方法的参数是WCF那边自定义的对象,这样有办法调用么,如果可以IOS应该怎么传参呢?请问有了解的么,...

最近做一个项目后端使用WCF接收Android手机拍照并带其它参数保存到服务器里;刚好把最近学习的WCF利用上,本以为是个比较简单的功能应该很好实现,没想到其中碰到不少问题,在网上搜索很久一直没有想到的解决方案,最后实现…

ABP vNext微服务架构详细教程——项目部署

1基础配置在之前的文章中,我们已经配置了Kubernetes集群并安装了管理工具Kubesphere,文章地址为:https://mp.weixin.qq.com/s/MgpdMv5A-fYxN7XY8N9Djw登录Kubesphere页面,打开工作台,在平台资源选项卡中点击“企业空间…

offsetTop和scrollTop的差别

近期想写个组件,结果被这两个属性搞的有点晕,查了下文档和资料,对这两个属性总结例如以下: 一直以来对offsetLeft,offsetTop,scrollLeft,scrollTop这几个方法非常迷糊,花了一天的时间…

quartz (一) 基于 Quartz 开发企业级任务调度应用

本文转自:http://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/ Quartz 基本概念及原理 Quartz Scheduler 开源框架 Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现。该项目于 2009 年被 Terracotta 收购&…

C# 11 中的参数 null 检查

C# 11 中的参数 null 检查IntroC# 11 将引入一个新的操作符 !! 来简化我们代码中的对于参数的 null 检查,昨天发布的 .NET 7 Preview 1 已经支持了这一语法,感兴趣的不妨来试一下吧,下面我们就来看一下如何使用吧Prepare如果你想在本地代码中…

cms的 php代码,KingCMS/PHP可执行代码

实例:Example :{king:title/}{king:content/}作者:{king:_author/}上面的代码没有什么特别的地方,但客户的要求有了变化,他想在详细页的内容开始前调用缩略图,没有则忽略。所以问题也来了,因为有的文章有缩略图,有的没…

CentOS单机安装k8s并部署.NET 6程序

学习云原生,k8s 是一个基础,为了做一些实验,单机部署是最方便的,下面将介绍在 CentOS 中单机安装 k8s ,并将一个 .NET 6 的程序发布到 k8s 中。环境宿主机:Mac 10.15.7CentOS版本:7.6内存&#…

php session缓存,扫盲:php session缓存至memcached中的方法

memcached是一套分布式的快取系统,当初是DangaInteractive为了LiveJournal所发展的,但被许多软件(如MediaWiki)所使用。这是一套开放源代码软件,以BSDlicens更改为:session.save_handler memcachesession.save_path"tcp://12…

MASA Framework - DDD设计(2)

Clean Architecture国内对于Clean Architecture的翻译很多,干净/整洁/清晰。但无论哪一种都说明了它简洁、清晰的特性。早期它长这样看到这张图的同学可能会对另外一张图有印象洋葱架构(Onion)现在长这样看起来好像是亲戚,它们的确也有着千丝万缕的关系分…

SpringMVC核心分发器DispatcherServlet分析[附带源码分析]

目录 前言DispatcherServlet初始化过程DispatcherServlet处理请求过程总结参考资料前言 SpringMVC是目前主流的Web MVC框架之一。 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 本…

WPF 展示视频修改为WriteableBitmap

WPF开发者QQ群:340500857由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 PS:有更好的方式欢迎推荐。接着上一篇,进行WriteableBitmap性能优化修改后运行对比如下:前(C…

linux之类似Windows的资源管理器gnome-system-monitor(可用这个杀死进程)

1、使用 直接运行下面命令gnome-system-monitor 如果没有安装用下面命令安装sudo apt-get install gnome-system-monitor 2、结果 可以点击右键然后杀死相关进程,这也是杀死进程的办法。

HttpClient异常处理手册

HttpClient异常处理手册 开源中国 发表于 2014-08-26 19:44:06异常处理 HttpClient的使用者在执行HTPP方法(GET,PUT,DELETE等),可能遇到会两种主要类型的异常: 传输异常协议异常并不是所有的异常都会传播给HttpClient的用户。Htt…

再读《精通css》02:选择器

2019独角兽企业重金招聘Python工程师标准>>> 1.2 为样式找到目标1、类型选择器用来选择特定类型的原素。比如p,a,h1等等。也叫元素选择器或简单选择器。2、后代选择器用来寻找特定元素或元素组的后代。后代选择器由两个选择器之间的空格表示。…