Unity+C# 客户端
UI界面根据个人喜好排版
图1 进入界面 图2 聊天界面
C#脚本代码如下:
Client_dxc.cs
using UnityEngine;
using System.Net.Sockets;
using System.Text;
using System;
using UnityEngine.UI;public class Client_dxc : MonoBehaviour
{public Text GetText;public InputField InputText;public InputField ZhuCeInputText;public Text chatText;public ScrollRect scrollRect;public GameObject DengLuJieMian;private string SetText;private Socket socket;private byte[] buffer = new byte[1024];void Start(){//AddressFamily 寻址类型 SocketType 套接字类型 ProtocolType 协议类型socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//连接服务器socket.Connect("127.0.0.1", 9999);Debug.Log("连接成功!");//开始接收信息readServer();}void Update(){huoqu();}//接收信息void readServer(){//Socket异步接收数据//buffer存储接收到的数据,offset从零开始计数,size要接收的字节数,SocketFlags 值的按位组合//ReceiveCallback一个用户定义的对象,其中包含接收操作的相关信息。当操作完成时,此对象会被传递给EndReceive(IAsyncResult)委托socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, null); }void ReceiveCallback(IAsyncResult iar){int len = socket.EndReceive(iar);if (len == 0){return;}string str = Encoding.UTF8.GetString(buffer, 0, len);SetText = str;//循环接收数据readServer();}//发送信息void sendServer(string msg){//将字符串转换为UTF-8编码的字节数组var outputBuffer = Encoding.UTF8.GetBytes(msg);//将数据异步发送到连接的 Socket//BoutputBuffer包含要发送的数据,SocketFlags 值的按位组合,callback:AsyncCallback 委托,state:Object 包含此请求的状态信息的对象socket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, null, null);}//获取信息输出到信息面板上public void huoqu(){if (SetText != ""){string addText = "\n " + SetText;//将获取的信息添加到信息面板中chatText.text += addText;SetText = "";//强制画布更新内容Canvas.ForceUpdateCanvases();//垂直滚动位置,以 0 到 1 之间的值表示,0 表示位于底部scrollRect.verticalNormalizedPosition = 0f;Canvas.ForceUpdateCanvases();}}//绑定“发送”按钮(群发消息)public void qunInput(){if (InputText.text != ""){string msg = "Mass:" + InputText.text + "\r\n";sendServer(msg);InputText.text = "";}}//绑定“进入”按钮(进入群聊注册用户名)public void ZhuCe(){if (ZhuCeInputText.text != ""){string msg = "userName:" + ZhuCeInputText.text + "\r\n";sendServer(msg);DengLuJieMian.SetActive(false);}}}
将脚本挂在Camera上,绑定对应组件及按钮
Java 服务器端
Main.java
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class Main {public static void main(String[] args) {try {//创建ServerSocket对象,监听9999端口ServerSocket serversocket = new ServerSocket(9999);System.out.println("----------服务器启动----------");System.out.println("开始监听:");//创建一个循环,使主线程持续监听while (true){//做一个阻塞,监听客户端Socket socket = serversocket.accept();//创建一个新的线程,将客户端socket传入Server server = new Server(socket);//启动线程server.start();}} catch (IOException e) {throw new RuntimeException(e);}}
}
Server.java
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Server extends Thread{//创建一个Map集合private static Map<String,Socket> map = new ConcurrentHashMap<>();//创建Socket对象private Socket socket;public Server(Socket socket){this.socket = socket;}//重写run方法public void run(){try {//获取客户端的输入流Scanner scanner=new Scanner(socket.getInputStream());String msg=null;//写一个循环来持续处理获取到的信息while (true) {//判断是否接收到了字符串if(scanner.hasNextLine()){//获取信息msg=scanner.nextLine();//处理客户端输入的字符串Pattern pattern=Pattern.compile("\r");Matcher matcher=pattern.matcher(msg);msg=matcher.replaceAll("");//判断字符串是不是以userName:为前缀的if(msg.startsWith("userName:")){//将字符串从:拆分开,后半部分存入userName中String userName=msg.split(":")[1];//注册该用户userEnroll(userName,socket);//将Map集合转换为Set集合Set<Map.Entry<String,Socket>> set=map.entrySet();//遍历集合,向所有用户发送消息for(Map.Entry<String,Socket> entry:set){//取得客户端的Socket对象Socket client=entry.getValue();//取得client客户端的输出流PrintStream printstream=new PrintStream(client.getOutputStream());//向客户端发送信息printstream.println(userName + "加入了群聊!");}continue;}//判断字符串是不是以Mass:为前缀的if(msg.startsWith("Mass:")){//拆分字符串,获取信息内容String str=msg.split(":")[1];//发送群聊信息MassSend(socket,str);}}}} catch (IOException e) {throw new RuntimeException(e);}}//注册用户private void userEnroll(String userName,Socket socket){//将键值对添加入Map集合中map.put(userName,socket);//打印日志System.out.println("[用户名为"+userName+"][客户端为"+socket+"]上线了!");System.out.println("积累进入人数为:"+map.size()+"人");}//发送消息private void MassSend(Socket socket,String msg) {try {//将Map集合转换为Set集合Set<Map.Entry<String, Socket>> set = map.entrySet();String SendName = null;//遍历Set集合找到发起群聊信息的用户for (Map.Entry<String, Socket> entry : set) {//判断集合中的值是否与发起群聊的用户相同if (entry.getValue().equals(socket)) {//获取键名SendName = entry.getKey();break;}}//遍历Set集合将群聊信息发给每一个客户端for (Map.Entry<String, Socket> entry : set) {//取得客户端的Socket对象Socket client = entry.getValue();//取得client客户端的输出流PrintStream printStream = new PrintStream(client.getOutputStream());//发送消息printStream.println(SendName + ":" + msg);}} catch (IOException e) {e.printStackTrace();}}}
应用测试
本次测试为本机测试,模拟多客户端环境
先启动服务器
然后双开Unity,进入两个用户
服务器端界面如下:
客户端聊天界面如下:
测试通过,可正常运行
将Java服务器端代码打包成jar包,发送到服务器上运行即可
将C#代码中的目标ip地址改为服务器ip,Unity客户端打包apk文件,发送到各个手机端安装运行即可