Unity 简单TCP通信实现
参考教程:【01_C#入门到精通】新手强烈推荐:C#开发课程,一整套课程_哔哩哔哩_bilibiliP178-P187
我由于毕设需要来学网络通信,属于完全没常识的小白,网上很多现成代码却不懂概念、不会改,现在总结一下踩过的坑,希望后来人少走弯路。
目录
TCP通信理解
服务端代码
客户端代码
如何获取本机ip地址
运行结果:
调试技巧
连接失败可能原因:
参考
TCP通信理解
纯小白建议看课程理解,讲得很通俗。
TCP的通信过程与打电话的过程大致相同,一方负责接听特定号码的电话,而另外一方则需要拨通这个电话号码,与对方建立连接。双方都可以通过听筒(socket输入流)接受对方的信息,通过话筒(socket输出流)向对方发送信息,并且在整个过程中信息的接收和发送是同时进行的。
Socket对应电话,IP地址和端口号对应电话号码。(通过IP地址可以定位到服务端,结合端口号可以定位到服务端里的一个应用程序)
客户端的Socket先连接到服务端负责监听的Socket。服务端检测到连接后创建一个负责通信的Socket,这个Socket通过Receive()和Send()函数和客户端的Socket收发消息。
服务端代码server.cs
(可以对照上面流程图来理解)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using System.IO;public class server : MonoBehaviour
{private string info = "NULL"; //状态信息private string recMes = "NULL"; //接收到的信息private int recTimes = 0; //接收到的信息次数 private string inputIp = "10.250.27.220"; //服务端ip地址;可以手动在服务端电脑-cmd- ipconfig 获取IPV4地址private string inputPort = "50000"; //端口值,大于50000比较保险private string inputMessage = "NULL"; //用以发送的信息 private Socket socketWatch; //用以监听的套接字private Socket socketSend; //用以和客户端通信的套接字private bool isSendData = false; //是否点击发送数据按钮private bool clickConnectBtn = false; //是否点击监听按钮void Start(){}// Update is called once per framevoid Update(){}//点击开始监听后执行private void ClickConnect(){try{int _port = Convert.ToInt32(inputPort); //端口号转为intclickConnectBtn = true; //点击了监听按钮//第一步:创建负责监听的SocketsocketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//参数说明:IPV4对应InterNetwork;Tcp对应Stream//用DNS类获得本机的IPV4地址,即IPHost.AddressList[2],是IPAddress格式IPHostEntry IPHost = Dns.Resolve(Dns.GetHostName());string _ip = Convert.ToString(IPHost.AddressList[2]);inputIp = _ip;//方便后面显示本机IPDebug.Log(" ip 地址是 :" + _ip);Debug.Log(" 端口号是 :" + _port);info = "ip地址是 : " + _ip + "端口号是 : " + _port;//第二步:为Socket寻址(命名),即绑定IP地址和端口号IPEndPoint point = new IPEndPoint(IPHost.AddressList[2], _port);socketWatch.Bind(point); //绑定Debug.Log("监听成功!");info = "监听成功";//第三步:开始监听socketWatch.Listen(10); //设置监听,最大同时连接10台//创建监听线程,这样防止卡死在socketWatch.Accept();Thread thread = new Thread(Listen);thread.IsBackground = true;thread.Start(socketWatch);//相当于socketwatch传到Listen()}catch { }}/// <summary>/// 等待客户端的连接 并且创建与之通信的Socket/// </summary>void Listen(object o)//线程接受的参数必须为object{try{Socket socketWatch = o as Socket;//将object强转为Socket格式while (true){socketSend = socketWatch.Accept(); //等待接收客户端连接,然后把这个连接赋给负责通信的SocketDebug.Log(socketSend.RemoteEndPoint.ToString() + ":" + "连接成功!");info = socketSend.RemoteEndPoint.ToString() + " 连接成功!";//第四步:接收消息和发送消息Thread r_thread = new Thread(Received); //开启一个新线程,执行接收消息方法r_thread.IsBackground = true;r_thread.Start(socketSend);Thread s_thread = new Thread(SendMessage); //开启一个新线程,执行发送消息方法s_thread.IsBackground = true;s_thread.Start(socketSend);}}catch { }}/// <summary>/// 服务器端不停的接收客户端发来的消息/// </summary>void Received(object o){try{Socket socketSend = o as Socket;while (true){byte[] buffer = new byte[1024 * 6]; //缓冲区?int len = socketSend.Receive(buffer); //实际接收到的有效字节数if (len == 0){break;}string str = Encoding.UTF8.GetString(buffer, 0, len);Debug.Log("接收到的消息:" + socketSend.RemoteEndPoint + ":" + str);recMes = str;recTimes ++;info = "接收到一次数据,接收次数为:" + recTimes;Debug.Log("接收数据次数:" + recTimes);}}catch { }}/// <summary>/// 服务器端不停的向客户端发送消息/// </summary>void SendMessage(object o){try{Socket socketSend = o as Socket;while (true){if(isSendData){isSendData = false;byte[] sendByte = Encoding.UTF8.GetBytes(inputMessage);Debug.Log("发送的数据为 :" + inputMessage);Debug.Log("发送的数据字节长度 :" + sendByte.Length);socketSend.Send(sendByte);//发送消息}}}catch { }}private void OnDisable(){Debug.Log("begin OnDisable()");if (clickConnectBtn){try{socketWatch.Shutdown(SocketShutdown.Both); //禁用Socket的发送和接收功能socketWatch.Close(); //关闭Socket连接并释放所有相关资源socketSend.Shutdown(SocketShutdown.Both); //禁用Socket的发送和接收功能socketSend.Close(); //关闭Socket连接并释放所有相关资源 }catch (Exception e){Debug.Log(e.Message);}}Debug.Log("end OnDisable()");}//交互界面void OnGUI(){GUI.color = Color.black;GUI.Label(new Rect(65, 10, 80, 20), "状态信息");GUI.Label(new Rect(155, 10, 80, 70), info);GUI.Label(new Rect(65, 80, 80, 20), "接收到消息:");GUI.Label(new Rect(155, 80, 80, 20), recMes);GUI.Label(new Rect(65, 120, 80, 20), "发送的消息:");inputMessage = GUI.TextField(new Rect(155, 120, 100, 20), inputMessage, 20);GUI.Label(new Rect(65, 160, 80, 20), "本机ip地址:");inputIp = GUI.TextField(new Rect(155, 160, 100, 20), inputIp, 20);GUI.Label(new Rect(65, 200, 80, 20), "本机端口号:");inputPort = GUI.TextField(new Rect(155, 200, 100, 20), inputPort, 20);if (GUI.Button(new Rect(65, 240, 60, 20), "开始监听")){ClickConnect();}if (GUI.Button(new Rect(65, 280, 60, 20), "发送数据")){isSendData = true;}}
}
客户端代码client.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.IO;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;public class client : MonoBehaviour
{private string staInfo = "NULL"; //状态信息private string inputIp = "10.250.27.220"; //输入服务端ip地址private string inputPort = "50000"; //输入服务端端口号public string inputMes = "NULL"; //发送的消息private int recTimes = 0; //接收到信息的次数private string recMes = "NULL"; //接收到的消息private Socket socketSend; //客户端套接字,用来链接远端服务器private bool clickSend = false; //是否点击发送按钮void Start(){}// Update is called once per framevoid Update(){}//建立链接private void ClickConnect(){try{int _port = Convert.ToInt32(inputPort); //int型端口号string _ip = inputIp; //创建客户端Socket,获得远程服务端ip和端口号socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ip = IPAddress.Parse(_ip);IPEndPoint point = new IPEndPoint(ip, _port);//连接socketSend.Connect(point);Debug.Log("连接成功 , " + " ip = " + ip + " port = " + _port);staInfo = ip + ":" + _port + " 连接成功";Thread r_thread = new Thread(Received); //开启新的线程,不停的接收服务器发来的消息r_thread.IsBackground = true;r_thread.Start();Thread s_thread = new Thread(SendMessage); //开启新的线程,不停的给服务器发送消息s_thread.IsBackground = true;s_thread.Start();}catch { }}/// <summary>/// 接收服务端返回的消息/// </summary>void Received(){while (true){try{byte[] buffer = new byte[1024 * 6];int len = socketSend.Receive(buffer);if (len == 0){break;}recMes = Encoding.UTF8.GetString(buffer, 0, len);Debug.Log("客户端接收到的数据 : " + recMes);recTimes ++;staInfo = "接收到一次数据,接收次数为 :" + recTimes;Debug.Log("接收次数为:" + recTimes);}catch { }}}/// <summary>/// 向服务器发送消息/// </summary>/// <param name="sender"></param>/// <param name="e"></param>void SendMessage(){try{while (true){if (clickSend) //如果点击了发送按钮{clickSend = false;string msg = inputMes;byte[] buffer = new byte[1024 * 6];buffer = Encoding.UTF8.GetBytes(msg);socketSend.Send(buffer);Debug.Log("发送的数据为:" + msg);}}}catch { }}private void OnDisable(){Debug.Log("begin OnDisable()");if (socketSend.Connected){try{socketSend.Shutdown(SocketShutdown.Both); //禁用Socket的发送和接收功能socketSend.Close(); //关闭Socket连接并释放所有相关资源}catch (Exception e){print(e.Message);}}Debug.Log("end OnDisable()");}//用户界面void OnGUI(){GUI.color = Color.black;GUI.Label(new Rect(65, 10, 60, 20), "状态信息");GUI.Label(new Rect(135, 10, 80, 60), staInfo);GUI.Label(new Rect(65, 70, 50, 20), "服务器ip地址");inputIp = GUI.TextField(new Rect(125, 70, 100, 20), inputIp, 20);GUI.Label(new Rect(65, 110, 50, 20), "服务器端口");inputPort = GUI.TextField(new Rect(125, 110, 100, 20), inputPort, 20);GUI.Label(new Rect(65, 150, 80, 20), "接收到消息:");GUI.Label(new Rect(155, 150, 80, 20), recMes);GUI.Label(new Rect(65, 190, 80, 20), "发送的消息:");inputMes = GUI.TextField(new Rect(155, 190, 100, 20), inputMes, 20);if (GUI.Button(new Rect(65, 230, 60, 20), "开始连接")){ClickConnect();}if (GUI.Button(new Rect(65, 270, 60, 20), "发送信息")){clickSend = true;}}
}
如果直接用我的代码,记得把客户端代码里ip地址改为自己电脑的ip
如何获取本机ip地址
搜索cmd(命令提示符)
输入ipconfig 回车
查看IPv4地址
运行结果:
分别在两台电脑上运行,
服务端:点击开始监听,没问题的话会显示“监听成功”
客户端:确保输入的ip和端口号和服务端保持一致,然后点击开始连接
服务端:成功的话会显示连接成功,然后输入发送的消息,点击发送数据
客户端:会显示接收到的消息
服务端:
客户端:
百度网盘链接,提取码:788j
调试技巧
测试服务端能不能连接,可以用telnet,不用另开一台电脑。
开启telnet功能:搜索启用windows功能-勾选telnet客户端
服务端开始监听后
cmd-输入telnet 服务端ip地址 端口号 回车 就能作为客户端连上,还能接受消息
示例:
telnet 10.250.27.220 50000
连接失败可能原因:
检查是否关闭防火墙;
是否关闭外网vpn;
ip地址是否正确;
两台机子要连同一局域网,正常两台机子ip地址只有后几位不一样,比如10.250.27.220和10.250.27.221
试试能不能ping通(在一台电脑cmd里输入ping 另一台电脑的ipv4地址 回车)
若有丢失/显示无法连接主机/请求超时就肯定不行。
参考
https://blog.csdn.net/qq_39748832/article/details/108540564?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162849488516780269835947%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162849488516780269835947&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-108540564.first_rank_v2_pc_rank_v29
《Windows 网络编程(第二版)》第13章
C# Socket实现两台电脑通信(一)_喏喏心的博客-CSDN博客_socket两台电脑通信
两台电脑TCP、UDP通信失败的可能原因_风语者和波波娃的博客-CSDN博客_两台电脑tcp连接不上
Unity 简单TCP通信实现相关推荐
- windows简单TCP通信 C++
尝试使用自定义struct和char发送信息.服务器根据客户端发送的不同消息进行回应. 在发送struct时先发送一个"Who"以便客户端确认. 服务器 #define WIN32 ...
- python 简单TCP通信实例 TCP断线自动重连 客户端服务端没有启动顺序
先启动客户端也可以 服务端: import socket import time#服务端HOST = '' PORT = 8888 ADDR = (HOST, PORT) BUFFSIZE = 102 ...
- linux下TCP通信简单实例
linux下TCP通信简单实例 基于TCP(面向连接)的socket编程,分为服务器端和客户端 服务器端的流程如下: (1)创建套接字(socket) (2)将套接字绑定到一个本地地址和端口上(bin ...
- socket PHP:详细简单的socket TCP通信PHP实现
如果你想直接运行程序实现效果:请直接看 3.3 本地服务器及客服端程序 1 背景介绍 目标:我希望通过套接字的TCP传输来搭建一个服务器,这个服务器的作用是:接受多个客户端的连接并完成他们的相互通信. ...
- Qt之超简单的TCP通信(自定义TCP通信类,含源码+注释)
文章目录 一.TCP通信示例图 二.TCP使用前的准备 三.自定义TCP通信类的两种方法 四.源码(含注释) TCP Server CTcpServer.h CTcpServer.cpp TCP Cl ...
- Linux系统下实现基于TCP/IP协议的简单Socket通信
Linux系统下实现基于TCP/IP协议的简单Socket通信 网络套接字Socket Socket概念 主机字节序和网络字节序 Sockaddr地址结构 Socket实现客户端.服务端通信 服务端程 ...
- QT5实现简单的TCP通信Client端
QT5实现简单的TCP通信Client端 基本步骤 完整代码 运行结果 最近学习了QT的TCP通信,与大家分享下刚开始写TCP通信的一些经验.下面简单写了个TCP客户端的小例程. 基本步骤 新建一个工 ...
- 网络编程 UDP通信的过程 TCP通信过程 多线程文件上传
网络概述 协议 在网络之间传出数据时需要按照指定的标准来传输,标准中规定了数据的格式.大小.传输的方式.传输速率.形成统一规范->按照规范开发的代码->协议(应用层.传输层.网络层.链路层 ...
- JAVA SE学习day_08:TCP通信、多线程(并联)
为了解决在程序中不同客户端在同一服务端中都可以进行数据发送的情况,要使用多线程将其进行并联操作 一.多线程 线程可以并发执行多个代码片段,使得这些代码片段直接运行是互不干涉的: 创建线程有两种方式, ...
最新文章
- 什么是棉绒,它如何节省您的时间?
- 完成登录功能,用session记住用户名
- 计算机操作系统课设总结,计算机操作系统课程设计
- c语言程序设计家庭收支类,家庭支出管理系统—c语言程序设计
- shell getopts
- C++ 泛型编程(一):模板基础:函数模板,类模板,模板原理,模板匹配规则
- [蓝桥杯][2017年第八届真题]小计算器(模拟)
- NET问答: 如何让 HttpClient 支持 Http 2.0 协议?
- “Chrome不能访问更新服务器(错误:3)”的解决方法
- 如何优化网站的响应时间
- 编程通用知识 系统编程
- 二进制GCD算法解析
- ArrayUtils
- Hybird Astar算法原理
- Python学习3-层次聚类
- python usb摄像头 截图_python实现摄像头远程截图功能
- ABAQUS常见错误(一)多根预应力钢绞线
- 雷达动目标显示(MTD)的理解
- ICCV 2019 Vehicle Re-identification with Viewpoint-aware Metric Learning
- B站直播的自研P2P实践 | 助力S12英雄联盟总决赛