使用Socket实现局域网内聊天室

news/2024/5/3 9:56:13

Socket的通信方式:
    Socket有TCP和UDP两种通信方式,我们可以根据具体的情况来选择。一般情况下,如果需要数据准确传输、不丢失,则选择TCP;反之,则选择UDP。
  参考文章:
      Socket的TCP和UDP连接

需要提前了解的Socket知识点:

  1. Client端输入的IP都是Server所在电脑的IP
  2. Server最好设置0.0.0.0这样无论迁移到哪个电脑上,都是那台电脑的IP
  3. Client和Server必须在同一个局域网之下,否则不能通信;如果要实现跨局域网通信,需要使用公网IP(通过阿里云等)

整个聊天室的代码分为两个模块:

  1. Server服务器模块:只能有一个,用来存储监听用户发送的内容
  2. Client客户模块:可以有多个,各个用户直接进行收发信息

以下是Server代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace Server
{class Program{static Socket socket = null;static void Main(string[] args){socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);socket.Bind(new IPEndPoint(IPAddress.Parse("0.0.0.0"), 60000));socket.Listen(10);Console.WriteLine("已启动侦听");Task.Run(Connect);Console.ReadLine();}static List<Socket> socketPool = new List<Socket>();static Socket acceptSocket = null;static void Connect(){while (true){try{acceptSocket = socket.Accept();var receiveSocket = acceptSocket;Console.WriteLine($"已接受连接:{receiveSocket.RemoteEndPoint}");socketPool.Add(receiveSocket);Task.Run(Receive);}catch (Exception ex){Console.WriteLine($"连接失败:{ex.Message}");}}}static void Receive(){var receiveSocket = acceptSocket;while (true){if (receiveSocket == null) continue;if (!receiveSocket.Connected) continue;try{byte[] buffer = new byte[1024];var len = receiveSocket.Receive(buffer);if (len < 1) continue;var msg = Encoding.UTF8.GetString(buffer, 0, len);Console.WriteLine($"来自 {receiveSocket.RemoteEndPoint} 的消息 {msg}");var responseBuffer = Encoding.UTF8.GetBytes($"来自{receiveSocket.RemoteEndPoint} 的消息:{msg}");// receiveSocket.Send(responseBuffer);//清理失效的连接for (int i = socketPool.Count - 1; i >= 0; i--){if (socketPool[i] == null || !socketPool[i].Connected){socketPool.RemoveAt(i);}}//广播消息(除了发送消息方之外的,全部广播)foreach (var s in socketPool){if (s != receiveSocket){s.Send(responseBuffer);}}buffer = null;}catch (Exception ex){Console.WriteLine(ex.Message + "\n");}}}}
}

以下是Client代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;/// <summary>
/// 客户端
/// </summary>
namespace WpfApp1
{/// <summary>/// MainWindow.xaml 的交互逻辑/// </summary>public partial class MainWindow : Window{Socket socket = null;public MainWindow(){InitializeComponent();btnConnect.Click += btnConnect_Click;btnSend.Click += btnSend_Click;Task.Run(Receive);}private void btnConnect_Click(object sender, RoutedEventArgs e){if (socket != null && socket.Connected){ShowMessage($"无需重复连接");return;}try{socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);ShowMessage($"LocalEndPoint={socket.LocalEndPoint},RemoteEndPoint={socket.RemoteEndPoint}");var ipEndPoint = new IPEndPoint(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text));socket.Connect(ipEndPoint);if (socket.Connected){ShowMessage($"成功连接到:{socket.RemoteEndPoint}");}}catch (Exception ex){ShowMessage($"连接失败:{ex.Message}");socket = null;}}private void btnSend_Click(object sender, RoutedEventArgs e){if (!socket.Connected){ShowMessage($"未连接,无法发送");return;}try{if (rtxtSend.Text != ""){var buffer = Encoding.UTF8.GetBytes(rtxtSend.Text);socket.Send(buffer);ShowMessage($"发送到:{socket.RemoteEndPoint},消息:{rtxtSend.Text}");}rtxtSend.Text = null;}catch (Exception ex){ShowMessage($"发送失败:{socket.RemoteEndPoint},{ex.Message}");socket = null;}}private void Receive(){while (true){if (socket == null) continue;if (!socket.Connected) continue;try{byte[] buffer = new byte[1024];if (buffer != null){int len = socket.Receive(buffer);if (len < 1) continue;string msg = Encoding.UTF8.GetString(buffer, 0, len);ShowMessage(msg);}buffer = null;}catch (Exception ex){ShowMessage(ex.Message + "\n");socket = null;}}}private void ShowMessage(string msg){///这是Winform的用法//子线程调用 //if (rtxtLog.InvokeRequired) //c#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的//{//    rtxtLog.Invoke(new Action(() => rtxtLog.Text += msg + "\n"));//}//主线程调用//else//{//    rtxtLog.Text += msg + "\n";//}if (!CheckAccess()) //WPF使用Dispatcher控制对消息泵的访问,而不是让每个控件负责访问UI线程。{Dispatcher.Invoke(new Action(() => rtxtLog.Text += msg + "\n"));}//主线程调用else{rtxtLog.Text += msg + "\n";}}}
}

http://www.mrgr.cn/p/53557011

相关文章

Vue 3 项目构建与效率提升:vite-plugin-vue-setup-extend 插件应用指南

一、Vue3项目创建 前提是已安装Node.js&#xff08;点击跳转Node官网&#xff09; npm create vuelatest这一指令将会安装并执行 create-vue&#xff0c;它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示&#xff1a; ✔ Projec…

Ubuntu 22.04 解决和 Windows 共享蓝牙设备的问题

我有一个 Airpods,连接到 WIndows 可以正常工作,但连接到 ubuntu 后会无法连接,只能删除设备选择重联,但是这又会导致 Windows 不能连接到耳机,只能也删除重新连接,费神费力。 要解决此问题,仍有两办法,让 Windows 将就 Linux,或者 Linux 将就 Windows,由于折腾注册表…

【STM32+HAL库】---- 驱动MAX30102心率血氧传感器

硬件开发板:STM32F407VET6 软件平台:cubemax+keil+VScode1 MAX30102心率血氧传感器工作原理 MAX30102传感器是一种集成了红外光源、光电检测器和信号处理电路的高度集成传感器,主要用于心率和血氧饱和度的测量。以下是MAX30102传感器的主要特点和工作原理:红外光源:MAX301…

OO第一次博客作业

OO第一次博客作业 目录1.前言 2.设计与分析 3.采坑心得 4.改进建议 5.总结 1.前言 正则表达式是java语言中一种非常重要的语言,他的重要性主要体现在以下方面: 1.高效的文本处理:正则表达式提供了一种高效的方式来处理文本数据。它可以快速地进行字符串的搜索、匹配、替换和…

【机器学习】数据变换---小波变换特征提取及应用案列介绍

引言 在机器学习领域&#xff0c;数据变换是一种常见且重要的预处理步骤。通过对原始数据进行变换&#xff0c;我们可以提取出更有意义的特征&#xff0c;提高模型的性能。在众多数据变换方法中&#xff0c;小波变换是一种非常有效的方法&#xff0c;尤其适用于处理非平稳信号和…

JVM——面试

https://juejin.cn/post/6998527815964426271 https://juejin.cn/post/7101120209540349959垃圾回收器 Serial(新生代)+ Serial Old(老年代) 特点:单线程垃圾回收器,垃圾回收过程中需要 STW,适用于运行在 Client 模式下的虚拟机; 新生代标记复制算法,老年代标记整理算法…

内存分配器

内存分配器 文章目录 内存分配器项目介绍内存池介绍池化技术内存池内存池主要解决的问题malloc 实现定长内存分配器怎么控制定长通过系统调用申请空间定长内存分配器中应该包含哪些成员变量内存池如何管理释放的对象内存分配器如何为我们申请对象定长内存池整体代码性能对比 高…

2024.4.19

2024.4.19 【你知道的都是真相。只可惜那些并不是真相的全部。】 Friday 三月十一 谷雨<BGM = "谷雨--音阙诗听"> AC :Answer Coarse,粗劣的答案 ​ CE :Compile Easily,轻松通过 ​ PC :Perfect Compile 完美的编译 ​ WA :Wonderful Answer,好答案 ​ RE :Ru…

Ubuntu 22.04 安装 Nvidia 驱动最方便安全的方式

刚安装好的 Ubuntu 22.04 没有 N 卡驱动,输入 nvidia-smi,提示没有此程序并推荐到 apt 安装。 但是,使用 apt 安装 nvidia 驱动会有极大概率出现启动黑屏和闪屏问题。 不如进入开始菜单,找到“附加驱动”:此处展示了可用的 Nvidia 驱动,选择自己想要的版本安装,"te…

Mockito单元测试

文章目录 Mockito单元测试 为什么要使用Mock?导入依赖import导入包使用Mock模拟测试某个类中的某个方法是否可以成功执行使用Mock模拟某个类的方法&#xff0c;自己给这个方法返回我们指定的值使用Mock模拟某个方法调用后会抛出指定的异常使用Mock模拟测试某个类中的某个方法(…

浅述.Net中的Hash算法(顺带对称、非对称算法)

【写在前面】 对称加密算法(只有一个私钥&#xff0c;比如DES【不推荐】、AES)&#xff1b; 非对称加密算法&#xff08;公钥与私钥&#xff0c;比如RSA&#xff09;&#xff1b; Hash算法也称为散列函数算法&#xff0c;任意长度的数据都转换为固定长度的字符串&#xff08…

【opencv】示例-videocapture_starter.cpp 从视频文件、图像序列或连接到计算机的摄像头中捕获帧...

/** * file videocapture_starter.cpp * brief 一个使用OpenCV的VideoCapture与捕获设备&#xff0c;视频文件或图像序列的入门示例 * 就像CV_PI一样简单&#xff0c;对吧&#xff1f; * * 创建于: 2010年11月23日 * 作者: Ethan Rublee * * 修改于: 2013年4月17日 * …

EasyExcel追加写入数据,分批查询多次写入场景下,注意使用方式【OOM警告】

使用.withTemplate(file) 将临时数据文件和真实数据文件合并的方式&#xff0c;在生产环境大批量数据下&#xff0c;完全不可取&#xff0c;有很高的内存溢出风险 伪代码 public static void writeAppend(String fileName) {String filePath "tempDir".concat(Fil…

GDExtension的C++示例

GDExtension的C++示例 本文按照官方文档,进行c++的GDExtension​插件开发,主要进行文档进行复刻,同时对文档中未涉及步骤进行补充 什么是GDExtension 除了GDScript​和C#​这两种脚本语言外,Godot​引擎可以执行其他编程语言编写的代码。目前有两种方式实现:C++模块与GDEx…

再见,晚晚

一、 尽管多少有些预感,但听到消息的时候,泪水还是几近夺眶而出。 “祝愿晚晚能坚持自己的梦想” “ymgg我们等生日会给晚晚一起录制一个祝福吧” 却是一语成谶,只留一个在屏幕前迷茫的我。 其实我已经很久没有完整地看一次晚晚的单播了。 但是,当看到晚晚的告别动态的时候…

探索人工智能绘图的奇妙世界

探索人工智能绘图的奇妙世界 人工智能绘图的基本原理机器之美&#xff1a;AI绘图作品AI绘图对艺术创作的影响未来展望与挑战图书推荐&#x1f449;AI绘画教程&#xff1a;Midjourney使用方法与技巧从入门到精通内容简介获取方式&#x1f449;搜索之道&#xff1a;信息素养与终身…

实验1 原型设计————一款法律咨询及科普类app

一、实验题目:原型设计 二、实验目的:掌握产品原型设计方法和相应工具使用。 三、实验要求 (1)对比分析墨刀、Axure、Mockplus等原型设计工具的各自的适用领域及优缺点(至少3条)。 1.墨刀: 适用领域: 产品设计,项目管理,可以利用墨刀绘制流程图,明确项目流程和时间节…

这是一篇有颜色的文章。

三张图片都是一样的,但大小不一样。

OOP题目集1~3的总结

目录(一)前言 (二)作业介绍 (三)算法与代码 (四)SourceMonitor代码分析 (五)自学内容 (六)总结一、前言介绍本篇博客的大致内容、写作目的、意义等本篇博客介绍如何使用Java语言基础和算法来解决题目问题,在此基础上进行对最近Java编程语言学习的总结 题目的难度为…

高效协作的OA系统:流程、消息和权限界面的核心设计要素

随着信息化时代的快速发展&#xff0c;办公自动化&#xff08;OA&#xff09;系统在企业管理中扮演着越来越重要的角色。OA 系统通过集成的信息管理、流程自动化和协同办公功能&#xff0c;极大地提升了企业的办公效率和管理水平。其中&#xff0c;流程、消息和权限界面设计作为…