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工程师标准>>> 有设计经验的一般都知道,版式设计需要对画面元素之间的关系有充分的认识,并能够在足够有限空间内合理布局,将图形与文字合理结合,下面就来给大家介绍下版式设计的方法。 如…

实现流水灯以间隔500ms的时间闪烁(系统定时器SysTick实现的精确延时)

/** ****************************************************************************** * file main.c * author iuc * version version 1.0 * date 2015-5-19 19:37:52 * brief 流水灯闪烁 ****************************************************************************** …

Android之解决360奇酷手机控制台打印全等级日志(默认只打印W、E等级日志)

1、问题 360奇酷手机很奇葩,默认安卓日志只打印等级W、E,现在我想打印所有等级日志 2、解决办法 1、在桌面点击拨号,在拨号盘输入“*20121220#”,进入工程模式; 2、点击“日志输出等级” 3、选择下面的选项 Log print enable 选 enable J…

php为什么在变量前加,php中变量前加、@等符号是什么意思?

如:$this->config &$config;如:if($_POST[add]){...}还有这个static function &instance() {static $object;if(empty($object)) {$object new self();}return $object;}分别都是什么意思和作用,请说明,感谢&#xf…

几个想法,有兴趣的可以深入下去

这些想法都是不成熟的想法,但是觉得会很有意思,先记于此,有兴趣的可以研究。 转载于:https://www.cnblogs.com/todoit/p/3793887.html

Windows获取CPU、内存和磁盘使用率脚本

获取CPU使用率脚本(vbs),另存为cpu.vbs:On Error Resume Next Set objProc GetObject("winmgmts:\\.\root\cimv2:win32_processorcpu0") Wscript.Echo "CPU 使用率: " & objProc.LoadPercentage & …

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

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

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

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

LeetCode之Construct the Rectangle

1、题目 For a web developer, it is very important to know how to design a web pages size. So, given a specific rectangular web page’s area, your job by now is to design a rectangular web page, whose length L and width W satisfy the following requirements:…

php ftp 创建文件夹失败,PHP使用FTP函数创建目录

这篇文章主要为大家详细介绍了PHP使用FTP函数创建目录,具有一定的参考价值,可以用来参考一下。经测试代码如下:// create directory through FTP connectionfunction FtpMkdir($path, $newDir) {$serverftp.yourserver.com; // ftp 地址$conn…

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框架接…

Angular 2 Decorators - 1

在我们深入了解 Angular 2 中 NgModule、Component、Injectable 等常见的装饰器之前,我们要先了解 TypeScript 中的装饰器。装饰器是一个非常酷的特性,最早出现在 Google 的 AtScript 中,它出现的目的是为了让开发者,开发出更容易…

LeetCode之Relative Ranks

1、题目 Given scores of N athletes, find their relative ranks and the people with the top three highest scores, who will be awarded medals: "Gold Medal", "Silver Medal" and "Bronze Medal". Example 1: Input: [5, 4, 3, 2, 1] Out…

php yii 插入,Yii2 批量插入、更新数据实例

在使用yii2开发项目时,有时候会遇到这样的情况:向后台发送多条数据,其中一些数据已经存在记录,只需要对其部分字段的值进行修改;而另一部分的数据则需要新添加进去.这就需要对添加的数据进行判断,其中一些执行update,剩下的执行insert代码如下,不对的地方请指教://批量更新,并将…

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

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

WCF中使用控件的委托,线程中的UI委托

UI界面&#xff1a; <Window x:Class"InheritDemo.Window1"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"Title"Window1" Height"300" Widt…

.NET6之MiniAPI(十七):缓存

缓存是空间换时间的一种做法&#xff0c;可以有效的提升响应时间&#xff0c;asp.net core引入了本地内存缓存和分布式缓存。先看一下本地内存缓存&#xff1a;using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Internal;var builder WebApplication.C…

Android之机端安装apk出现INSTALL_FAILED_INSUFFICIENT_STORAGE错误的解决方法

1、错误 INSTALL_FAILED_INSUFFICIENT_STORAGE 2、解决办法 是由于手机存储不足了&#xff0c;删除一些app,给手机留下足够的空间即可。

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

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