1.结合现状 功能分析

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

2.图说代码

代码细说:

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

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

客户端定义了一个字段

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

操作流程:

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

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

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

3.代码实现

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

主函数:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ServerTest
{
class Program
{
static void Main(string[] args)
{
// 调用构造函数,使用Start方法
ServerControl server = new ServerControl();
server.Start(); Console.ReadKey();
}
}
}

ServerControl类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ServerTest
{
public class ServerControl
{
// 声明变量(使用Socket需using System.Net.Sockets;)
private Socket serverSocket;
// 声明一个集合
private List<Socket> clientList; // 自定义有参构造函数,包含两个方法(IP地址,流程传输方式,TCP协议)
public ServerControl()
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientList = new List<Socket>();
} // 创建启动方法(IPEndPoint用于指定地址及端口初始化,需using System.Net;)
public void Start()
{
// 服务器启动
// 绑定IP地址(为任意IP)与端口(设置为12345)
serverSocket.Bind(new IPEndPoint(IPAddress.Any,));
serverSocket.Listen();
Console.WriteLine("服务器启动成功"); // 开启线程:目的实现服务器和客户端一对多连接
Thread threadAccept = new Thread(Accept);
threadAccept.IsBackground = true;
threadAccept.Start();
}
// Accept方法测试:接收客户端连接
private void Accept()
{
// 接收客户端方法,会挂起当前线程(.RemoteEndPoint表示远程地址)
Socket client = serverSocket.Accept();
IPEndPoint point = client.RemoteEndPoint as IPEndPoint;
Console.WriteLine(point.Address + "[" + point.Port + "] 连接成功!");
clientList.Add(client); // 开启一个新线程线程,实现消息多次接收
Thread threadReceive = new Thread(Receive);
threadReceive.IsBackground = true;
threadReceive.Start(client); // 尾递归
Accept();
} // Receive方法的使用测试
// 接收客户端发送过来的消息,以字节为单位进行操作
// 该方法会阻塞当前线程,所以适合开启新的线程使用该方法
// Accept()中将Receive作为线程传递对象,所以要注意一点,使用线程传递对象只能是object类型的!!
private void Receive(object obj)
{
// 将object类型强行转换成socket
Socket client = obj as Socket; IPEndPoint point = client.RemoteEndPoint as IPEndPoint; // 此处的异常抛出主要针对客户端异常的问题
// 比如,客户端关闭或者连接中断
// 程序会停留在int msgLen = client.Receive(msg);这段代码,而导致无法继续往下走
try
{
byte[] msg = new byte[];
// 实际接收到字节数组长度,该方法会阻塞当前线程,即(client.Receive(msg)开始挂起)
// 同时,这里还是尾递归挂起处
int msgLen = client.Receive(msg);
// 将msg装换成字符串
string msgStr = point.Address + "[" + point.Port + "]:" + Encoding.UTF8.GetString(msg, , msgLen);
Console.WriteLine(msgStr); // 调用广播函数
Broadcast(client,msgStr);
// 尾递归实现多条消息的接收;和while同理。
Receive(client);
}
catch
{
Console.WriteLine(point.Address + "[" + point.Port + "]积极断开");
// 若客户端中断,则将他在集合中删除
clientList.Remove(client);
}
} private void Broadcast(Socket clientOther,string msg)
{
foreach(var client in clientList)
{
if(client == clientOther)
{
// 不做任何响应
}
else
{
client.Send(Encoding.UTF8.GetBytes(msg));
}
}
}
}
}

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

主函数:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ClientTest
{
class Program
{
static void Main(string[] args)
{
// 调用构造函数
ClientControl client = new ClientControl();
// 输入本机IP与端口号
client.Connect("129.211.7.135", );
// 启动send方法
client.Send(); Console.ReadKey();
}
}
}

ClientControl类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ClientTest
{
public class ClientControl
{
// 声明变量
private Socket clientSocket; // 自定义有参构造方法((IP地址,流程传输方式,TCP协议))
public ClientControl()
{
clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
} // 创建通过IP与端口号连接的方法
public void Connect(string ip,int port)
{
clientSocket.Connect(ip, port);
Console.WriteLine("连接服务器成功"); // 客户端接收服务器消息的线程
Thread threadReceive = new Thread(Receive);
threadReceive.IsBackground = true;
threadReceive.Start();
} // 用于测试服务器向客户端返回一条消息
private void Receive()
{
while(true)
{
try
{
// 用于接收服务器的回复信息
byte[] msg = new byte[];
int msgLen = clientSocket.Receive(msg);
Console.WriteLine("服务器:"+Encoding.UTF8.GetString(msg,,msgLen));
}
// 异常处理方法
catch
{
Console.WriteLine("服务器积极拒绝!!");
// 退出while循环
break;
}
}
} // Send方法测试:即发送消息,以字节为单位
public void Send()
{
Thread threadSend = new Thread(ReadAndSend);
// 将该线程设为非后台线程。
// threadSend.IsBackground = true;
threadSend.Start();
} private void ReadAndSend()
{
// 提示操作方法
Console.WriteLine("请输入发送至服务器的内容或者输入quit退出");
// 输入内容
string msg = Console.ReadLine();
// 非退出情况下操作方式,使用while可以持续不断的接收用户输入
while (msg != "quit")
{
clientSocket.Send(Encoding.UTF8.GetBytes(msg));
msg = Console.ReadLine();
}
}
}
}

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. sed练习,一些sed常用方法

    1.复制/etc/rc.d/rc.local 文件至/tmp目录,将/tmp/rc.sysinit文件中的以至少一个空白字符开头的行的行首加#. sed -ri 's/^ +/#/g'  rc.loc ...

  2. MATLAB知识-解决因缺少libsvm 而运行出现Y must be a vector or a character array.

    matlab版本R2014b 最近运行一个使用svmtrain的程序,出现以下错误: 这是因为是在设定路径里面没有libsvm.辛亏有一位师姐的电脑里面有libsvm的包,我直接用了,这样就不需要下载 ...

  3. 【计算机视觉】Object Proposal之BING++

    本文是对 BING 算法的升级,主要是在快的同时保持定位精度  两个 + 分别对应: edge-based recursive boxes as one "+", and MTSE ...

  4. 最新 梦网科技java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.梦网科技等10家互联网公司的校招Offer,因为某些自身原因最终选择了梦网科技.6.7月主要是做系统复习.项目复盘.Leet ...

  5. jenkins相关war包下载

    1.jenkins的war包下载地址 地址:http://mirrors.jenkins-ci.org/,打开链接后,表格有war列,Releases行是短期更新包.LTS是长期更新包.一般选择Rel ...

  6. [学习笔记] Blender 常用工具 移动与旋转,缩放, 变换

    点击上面的移动图标之后,可在X,Y,Z轴移动物体. shift+S 之后,可有更多的移动选项. 旋转:可沿X,Y, Z 进行旋转 缩放 还可输入缩放的具体数值,更精确. 变换 可同时做移动.旋转.缩放 ...

  7. JAVA_split 字符串按照 . 分割

    split 按照 . 分割字符串时 需要进行转义 代码: String[] str = obj_str.split("\\.") split 按照 \ 分割字符串时 需要多次转义 ...

  8. C语言细节

    一些常见细节 int *p[]和 int (*p)[] 的区别 int *p[4]; //定义一个指针数组,该数组中每个元素是一个指针,每个指针指向哪里就需要程序中后续再定义了. int (*p)[4 ...

  9. C++中静态成员函数和普通成员函数存储方式相同

    先从一个示例查看类的创建过程中,静态成员函数和普通成员函数的存储区别. #include "stdafx.h" #include<iostream> #include& ...

  10. 第2章:LeetCode--第二部分

    本部分是非Top的一些常见题型及不常见题 LeetCode -- Longest Palindromic Substring class Solution { public: int isPalind ...