C#基于Socket实现简单聊天室功能
作者:Csoap994 发布时间:2023-05-09 01:17:32
因为这段时间在学习Socket,所以就试着写了一个简单的聊天室。主要分为服务器端和多个客户端。利用服务器端作数据中转站,实现消息群发。
1、服务器端有两个类:
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
namespace 聊天室_Socket_TCP_服务器端
{
class Program
{
static List<Client> clients = new List<Client>();
static List<Client> notClients = new List<Client>();
/// <summary>
/// 广播消息
/// </summary>
/// <param name="message"></param>
public static void CastMessageTOAllConnetedClients(string message)
{
foreach (var client in clients)
{
if (client.Conneted)
{
client.CastMessage(message);
}
else
{
notClients.Add(client);
}
}
foreach (var temp in notClients)
{
clients.Remove(temp);
}
}
static void Main(string[] args)
{
Socket tcpSever = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpSever.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.2"), 8899));
tcpSever.Listen(100);//监听是否有客户端发起连接
Console.WriteLine("Begin to listen...");
while (true)
{
Socket clientSocket = tcpSever.Accept();
if (clientSocket!=null)
{
Console.WriteLine("A client has connneted...");
Client client = new Client(clientSocket);//将每个新创建的连接通信放于client类做通信
clients.Add(client);
}
}
Console.ReadKey();
}
}
}
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace 聊天室_Socket_TCP_服务器端
{
/// <summary>
/// 利用该类和客户端做通信
/// </summary>
class Client
{
public Socket clientSocket;
private Thread mesManageTherad;
private byte[] bufffer=new byte[20];
public Client(Socket soc)
{
clientSocket = soc;
//由于消息是不断发送的,需要多次进行处理。这里开一个线程,专门用来处理消息。
mesManageTherad = new Thread(MessageSendFromClient);
mesManageTherad.Start();
}
private void MessageSendFromClient()
{
//开启的线程一直检测客户端客户端发过来的消息
while (true)
{
//判断连接是否断开, SelectMode.SelectRead读状态模式。
//poll已断开返回true
if (clientSocket.Poll(10,SelectMode.SelectRead)==true)
{
clientSocket.Close();
break;//终止本线程
}
int byteNum = clientSocket.Receive(bufffer);//从客户端接受消息
string mes = Encoding.UTF8.GetString(bufffer, 0 , byteNum);
Console.WriteLine("客户端发送过来的消息:"+mes);
//广播消息出去给每个客户端
Program.CastMessageTOAllConnetedClients(mes);//对CastMessage的一层封装
}
}
/// <summary>
/// Send messages to Clients
/// </summary>
public void CastMessage(string message)
{
byte[] data = Encoding.UTF8.GetBytes(message);
clientSocket.Send(data);
}
/// <summary>
/// 判断是否断开连接
/// </summary>
public bool Conneted
{
get
{
return clientSocket.Connected;
}
}
}
}
服务器端逻辑:
这里的服务器主要负责建立连接,接受客户端消息,广播客户端发来的消息。
服务器通过socket对象绑定服务器IP和相应端口号(端口号自己开,没有被其他软件占用就好),通过Listen监听和服务器socket对象的Accept方法捕捉连接到服务器的客户端socket,将捕捉到的客户端socket放入List集合中方便统一管理和后面的消息群发。
关于捕捉到的客户端socket的逻辑处理放在了Client类中统一管理。
Client类将收到客户端的消息进行接受,在Client中开启一个线程用于不断得检测是否有新消息从客户端发送过来,若有消息发送过来则通过CastMessageTOAllConnetedClients方法(对socket对象的Send方法的封装)发送给每一个客户端。
2.客户端
客户端是在Unity中使用NGUI插件简单开发的一个聊天界面。把脚本挂在NGUI控件上即可。客户端主要负责显示消息,发送消息,接收消息。
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
public class ChatManager : MonoBehaviour {
private string _ipAdress = "192.168.1.2";
private int _port=8899;
EndPoint remotPoint;
Socket clientSocket;
public UIInput buttonInput;
private bool isCanSend=false;
private string buttonMessage=null;
Thread receiveThread;
byte[] bufferReceive = new byte[1024];
public UILabel chatWindowLable;
private string message = "";//默认为空串
// Use this for initialization
void Start () {
ConnetToSever(_ipAdress, _port);//与服务器建立连接
}
// Update is called once per frame
void Update () {
if (message!=null&&message!="")
{
chatWindowLable.text += "\n" + message;
message = "";//清空消息
}
}
void ConnetToSever(string ipadress,int port)
{
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
remotPoint = new IPEndPoint(IPAddress.Parse(ipadress),port);
//建立连接
clientSocket.Connect(remotPoint);
//因为是一直在准备接受的状态,所以开启一个线程来负责处理接受消息
receiveThread = new Thread(ReceiveMessageFormSever);
receiveThread.Start();
}
private new void SendMessage(string message)
{
byte [] buffer= Encoding.UTF8.GetBytes(message);
clientSocket.SendTo(buffer,remotPoint);
}
public void OnSendButtonClickS()
{
if (buttonInput.value!=null)
{
buttonMessage = buttonInput.value;
}
else
{
buttonMessage = "输入框为空!";
}
SendMessage(buttonMessage);
buttonInput.value = "";
}
private void ReceiveMessageFormSever()
{
while (true)
{
if (clientSocket.Connected)
{
int length = clientSocket.Receive(bufferReceive);
message = Encoding.UTF8.GetString(bufferReceive, 0, length);
//ps:不要在单独的线程里面操作unity组件
}
else
{
break;
}
}
}
}
在客户端中同样有一个socket对象,这个对象通过ConnetToSever方法连接到服务器。在这里,假如某个客户端通过输入框输入文本被客户端脚本捕捉,它将以流的方式发送到服务器,服务器接受到文本,并在服务器端将文本群发至每个客户端。服务器开设了一个线程专门用于捕捉客户端发来的消息,当然,客户端也有相应的线程时刻捕捉服务器发来的消息。消息被客户端捕捉到了,就可以显示在各自的客户端界面上了。
来源:https://blog.csdn.net/Csoap2/article/details/98795484


猜你喜欢
- 1.Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。2.Spring I
- JavaWeb 使用DBUtils实现增删改查1、创建C3p0Utils类创建cn.itcast.jdbc.utils包代码如下:packa
- 目录定义数据库表访问表中的数据插入数据查询数据创建数据库测试 DaoRoom 是 SQLite 的封装,它使 Android 对数据库的操作
- Java 分割字符串java.lang.String 的 split() 方法, JDK 1.4 or laterpublic
- 安装完jdk环境后,编写第一个java程序hello.java:public class hello{
- 本文实例讲述了Java Swing实现让窗体居中显示的方法。分享给大家供大家参考,具体如下:Swing组件是AWT组建的增强组件,是功能强大
- “深入浅出,人人都是程序员”开发过android手机的同学都知道在eclipse中可以直接查找到SHA1值,但是使用intellij ide
- 1.拉取centos镜像docker pull centos:72.基于拉取到的镜像运行一个容器docker run -it --name
- 简单认识一下Startupnowinandroid项目作为目前google官方来演示MAD(现代Android开发技术)的示例项目,里面大量
- 直接用javaw.exe想打开aspectj-1.9.4.jar安装aspectJ选Java™ Platform SE binary提示JV
- 前言我们之前讲述的动画都需要主动触发或者是重复执行,那有没有自己触发动画的组件呢?这样我们就可以在 StatelessWidget 里直接使
- 本文实例讲述了Android开发之FloatingActionButton悬浮按钮基本使用、字体、颜色用法。分享给大家供大家参考,具体如下:
- 前言以前我们还需要手写数据库设计文档、现在可以通过引入screw核心包来实现Java 数据库文档一键生成。话不多说、直接上代码演示。支持的数
- 根据poi接收controller层的excel文件导入 可使用后缀
- /// 构造随机数 种子static int GetRandomSeed(){ byte[] byt
- 打jar包实现分离依赖lib和配置为了业务需要配置文件和jar分离,便于使用者修改配置信息,在网上找了很久,最终找到一个简单有效的方法。操作
- 了解JVM内存结构的目的在Java的开发过程中,因为有JVM自动内存管理机制,不再需要像在C、C++开发那样手动释放对象的内存空间,不容易出
- 有很多应用场景,用到了接口动态实现,下面举几个典型的应用:1、mybatis / jpa 等orm框架,可以在接口上加注解进行开发,不需要编
- 本文实例为大家分享了C#使用Aforge调用摄像头拍照的具体代码,供大家参考,具体内容如下一、新建一个Winform项目二、使用Nuget添
- 以最终客户的角度来看,JAR文件就是一种封装,他们不需要知道jar文件中有多少个.class文件,每个文件中的功能与作用,同样可以得到他们希