1.结合现状 功能分析

该功能基于上个项目的改进,主要是通过对服务器端代码的修改,以及对客户端作少许修改,实现开启多客户端时,一个客户端发送消息,达到对所有客户端广播的效果。可参考网吧里的点歌系统,比如某某用户在网吧点了一首歌,其他用户电脑的左下角都会弹出一个某某用户点了一首七里香,或者游戏里面的频道聊天,每个人发完消息后,聊天室里的人都知道你发的消息了,就像下图一样,这也正是做这个功能的初衷吧。

2.图说代码

代码细说:

服务器里面定义了两个字段,一个用于服务器与客户端的连接,另一个目的在于做一个接受广播的客户端表单。

服务器里有四个函数,分别对应着客户端监听、客户端连接、接受客户端消息和发报广播。

客户端定义了一个字段

客户端包含4个函数,分别为建立连接,接受广播,非后台的发送消息线程、发送消息四部分

操作流程:

1)开启服务器,即黑线①的过程,启动监听。

2)开启客户端,自动根据IP连接服务器,即绿线②。

3)客户端1发送消息至服务器,服务器广播消息至客户端2,即红线③。

3.代码实现

服务器端包含一个主函数和一个ServerControl类

主函数:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace ServerTest
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. // 调用构造函数,使用Start方法
  14. ServerControl server = new ServerControl();
  15. server.Start();
  16.  
  17. Console.ReadKey();
  18. }
  19. }
  20. }

ServerControl类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Text;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9.  
  10. namespace ServerTest
  11. {
  12. public class ServerControl
  13. {
  14. // 声明变量(使用Socket需using System.Net.Sockets;)
  15. private Socket serverSocket;
  16. // 声明一个集合
  17. private List<Socket> clientList;
  18.  
  19. // 自定义有参构造函数,包含两个方法(IP地址,流程传输方式,TCP协议)
  20. public ServerControl()
  21. {
  22. serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  23. clientList = new List<Socket>();
  24. }
  25.  
  26. // 创建启动方法(IPEndPoint用于指定地址及端口初始化,需using System.Net;)
  27. public void Start()
  28. {
  29. // 服务器启动
  30. // 绑定IP地址(为任意IP)与端口(设置为12345)
  31. serverSocket.Bind(new IPEndPoint(IPAddress.Any,));
  32. serverSocket.Listen();
  33. Console.WriteLine("服务器启动成功");
  34.  
  35. // 开启线程:目的实现服务器和客户端一对多连接
  36. Thread threadAccept = new Thread(Accept);
  37. threadAccept.IsBackground = true;
  38. threadAccept.Start();
  39. }
  40. // Accept方法测试:接收客户端连接
  41. private void Accept()
  42. {
  43. // 接收客户端方法,会挂起当前线程(.RemoteEndPoint表示远程地址)
  44. Socket client = serverSocket.Accept();
  45. IPEndPoint point = client.RemoteEndPoint as IPEndPoint;
  46. Console.WriteLine(point.Address + "[" + point.Port + "] 连接成功!");
  47. clientList.Add(client);
  48.  
  49. // 开启一个新线程线程,实现消息多次接收
  50. Thread threadReceive = new Thread(Receive);
  51. threadReceive.IsBackground = true;
  52. threadReceive.Start(client);
  53.  
  54. // 尾递归
  55. Accept();
  56. }
  57.  
  58. // Receive方法的使用测试
  59. // 接收客户端发送过来的消息,以字节为单位进行操作
  60. // 该方法会阻塞当前线程,所以适合开启新的线程使用该方法
  61. // Accept()中将Receive作为线程传递对象,所以要注意一点,使用线程传递对象只能是object类型的!!
  62. private void Receive(object obj)
  63. {
  64. // 将object类型强行转换成socket
  65. Socket client = obj as Socket;
  66.  
  67. IPEndPoint point = client.RemoteEndPoint as IPEndPoint;
  68.  
  69. // 此处的异常抛出主要针对客户端异常的问题
  70. // 比如,客户端关闭或者连接中断
  71. // 程序会停留在int msgLen = client.Receive(msg);这段代码,而导致无法继续往下走
  72. try
  73. {
  74. byte[] msg = new byte[];
  75. // 实际接收到字节数组长度,该方法会阻塞当前线程,即(client.Receive(msg)开始挂起)
  76. // 同时,这里还是尾递归挂起处
  77. int msgLen = client.Receive(msg);
  78. // 将msg装换成字符串
  79. string msgStr = point.Address + "[" + point.Port + "]:" + Encoding.UTF8.GetString(msg, , msgLen);
  80. Console.WriteLine(msgStr);
  81.  
  82. // 调用广播函数
  83. Broadcast(client,msgStr);
  84. // 尾递归实现多条消息的接收;和while同理。
  85. Receive(client);
  86. }
  87. catch
  88. {
  89. Console.WriteLine(point.Address + "[" + point.Port + "]积极断开");
  90. // 若客户端中断,则将他在集合中删除
  91. clientList.Remove(client);
  92. }
  93. }
  94.  
  95. private void Broadcast(Socket clientOther,string msg)
  96. {
  97. foreach(var client in clientList)
  98. {
  99. if(client == clientOther)
  100. {
  101. // 不做任何响应
  102. }
  103. else
  104. {
  105. client.Send(Encoding.UTF8.GetBytes(msg));
  106. }
  107. }
  108. }
  109. }
  110. }

客户端包含一个主函数和一个ClientControl类

主函数:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace ClientTest
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. // 调用构造函数
  14. ClientControl client = new ClientControl();
  15. // 输入本机IP与端口号
  16. client.Connect("129.211.7.135", );
  17. // 启动send方法
  18. client.Send();
  19.  
  20. Console.ReadKey();
  21. }
  22. }
  23. }

ClientControl类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8.  
  9. namespace ClientTest
  10. {
  11. public class ClientControl
  12. {
  13. // 声明变量
  14. private Socket clientSocket;
  15.  
  16. // 自定义有参构造方法((IP地址,流程传输方式,TCP协议))
  17. public ClientControl()
  18. {
  19. clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
  20. }
  21.  
  22. // 创建通过IP与端口号连接的方法
  23. public void Connect(string ip,int port)
  24. {
  25. clientSocket.Connect(ip, port);
  26. Console.WriteLine("连接服务器成功");
  27.  
  28. // 客户端接收服务器消息的线程
  29. Thread threadReceive = new Thread(Receive);
  30. threadReceive.IsBackground = true;
  31. threadReceive.Start();
  32. }
  33.  
  34. // 用于测试服务器向客户端返回一条消息
  35. private void Receive()
  36. {
  37. while(true)
  38. {
  39. try
  40. {
  41. // 用于接收服务器的回复信息
  42. byte[] msg = new byte[];
  43. int msgLen = clientSocket.Receive(msg);
  44. Console.WriteLine("服务器:"+Encoding.UTF8.GetString(msg,,msgLen));
  45. }
  46. // 异常处理方法
  47. catch
  48. {
  49. Console.WriteLine("服务器积极拒绝!!");
  50. // 退出while循环
  51. break;
  52. }
  53. }
  54. }
  55.  
  56. // Send方法测试:即发送消息,以字节为单位
  57. public void Send()
  58. {
  59. Thread threadSend = new Thread(ReadAndSend);
  60. // 将该线程设为非后台线程。
  61. // threadSend.IsBackground = true;
  62. threadSend.Start();
  63. }
  64.  
  65. private void ReadAndSend()
  66. {
  67. // 提示操作方法
  68. Console.WriteLine("请输入发送至服务器的内容或者输入quit退出");
  69. // 输入内容
  70. string msg = Console.ReadLine();
  71. // 非退出情况下操作方式,使用while可以持续不断的接收用户输入
  72. while (msg != "quit")
  73. {
  74. clientSocket.Send(Encoding.UTF8.GetBytes(msg));
  75. msg = Console.ReadLine();
  76. }
  77. }
  78. }
  79. }

4.实现过程

该过程将服务器部署至腾讯云服务器,分别在腾讯云服务器和本地PC上各开启2个客户端演示广播过程。

若没有多余的电脑或者云服务器,可将客户端主函数里面的IP地址代码改为127.0.0.1,即可完成本地测试。

一个基于TCP/IP的小项目,实现广播消息的功能。(超详细版)的更多相关文章

  1. 一个基于TCP/IP的服务器与客户端通讯的小项目(超详细版)

    1.目的:实现客户端向服务器发送数据 原理: 2.建立两个控制台应用,一个为服务器,用于接收数据.一个为客户端,用于发送数据. 关键类与对应方法: 1)类IPEndPoint: 1.是抽象类EndPo ...

  2. 基于TCP/IP协议的C++网络编程(API函数版)

    源代码:http://download.csdn.net/detail/nuptboyzhb/4169959 基于TCP/IP协议的网络编程 定义变量——获得WINSOCK版本——加载WINSOCK库 ...

  3. 一个基于ES5的vue小demo

    由于现在很多vue项目都是基于ES6开发的,而我学vue的时候大多是看vue官网的API,是基于ES5的,所以对于刚接触项目的我来说要转变为项目的模块化写法确实有些挑战.因此,我打算先做一个基于ES5 ...

  4. JAVA Socket 底层是怎样基于TCP/IP 实现的???

    首先必须明确:TCP/IP模型中有四层结构:       应用层(Application Layer).传输层(Transport  Layer).网络层(Internet Layer  ).链路层( ...

  5. 写一个基于TCP协议套接字,服务端实现接收客户端的连接并发

    ''' 写一个基于TCP协议套接字,服务端实现接收客户端的连接并发 ''' client import socket import time client = socket.socket() clie ...

  6. 基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  7. zabbix(一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案)

    zabbix 是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以让系统管理员快速定位/解决 ...

  8. [转] Linux TCP/IP网络小课堂:net-tools与iproute2大比较

    PS:netstat选项是-planet,方便记忆 http://os.51cto.com/art/201409/450886.htm 如今许多系统管理员仍结合使用ifconfig.route.arp ...

  9. 20181225 基于TCP/IP和基于UDP/IP的套接字编程

    一.TCP/IP的套接字编程 服务器端代码: import  socket​server = socket.socket() # 默认是基于TCP# 基于TCP的对象serve=socket.sock ...

随机推荐

  1. 给引入页面的js和css资源加上版本号,防止浏览器缓存资源

    最近因为在做前端开发的相关工作,每次发布新版本以后,不到5分钟,测试童鞋一个接一个的抱怨说BUG根本就没有修改,这个时候你说的最多的话就是“清缓存!!清页面缓存!!你没有清缓存!!你清理了页面缓存就对 ...

  2. dubbo探究

    一 占位 待整理.. 二 问题汇总 1 谈谈dubbo的超时重试 dubbo 启动时默认有重试机制和超时机制.如果在一定的时间内,provider没有返回,则认为本次调用失败.重试机制出现在调用失败时 ...

  3. Spring Cloud 中注册中心Eureka客户端配置

    注册中心配置客户端(注册一个虚拟的商品服务) 一.新建项目:     1.创建一个SpirngBoot应用,增加服务注册和发现依赖     2.模拟商品信息,存储在内存中     3.开发商品列表接口 ...

  4. MySQL 使用tee记录语句和输出日志

    在mysql命令行中,使用tee命令,可以记录语句和输出到指定文件.在debugging时会很有用.每执行一条语句,mysql都会讲执行结果刷新到指定文件.Tee功能只在交互模式生效. mysql&g ...

  5. 怎样手写实现 new 命令的功能

    实现new命令的功能, 首先需要知道new命令都做了些什么: 第一步: 创建一个空对象, 作为实例对象的容器; 第二步: 将空对象的prototype指向构造函数的prototype; 第三步: 将空 ...

  6. 将自定义dockerfile生成的image推送到仓库中

    本文为以阿里云的案例方法,其他方法未尝试 1.注册阿里云账号 2.设置密码并通过docker登录 3.创建命名空间 4.创建镜像仓库 创建仓库时,需要选择代码源的仓库储存的方式,这里我用的是gitHu ...

  7. 修改ubuntu设备名

    修改ubuntu设备名   执行如下命令:   sudo sed -i 's/当前设备名/新设备名/' /etc/hostname sudo sed -i 's/当前设备名/新设备名/' /etc/h ...

  8. nlopt 二次优化

    /* * main.c * * Created on: Oct 9, 2018 * Author: lgh */ #include <stdio.h> #include <math. ...

  9. java玩转zip压缩包

    首先将相关jar包引入pom.xml中 <!-- 解压zip --> <dependency> <groupId>org.apache.ant</groupI ...

  10. js检测是不是数字

    function isValueNumber(value) { var reg = (/(^-?[0-9]+\.{1}\d+$)|(^-?[1-9][0-9]*$)|(^-?0{1}$)/); var ...