Skcket:套接字
Socket怎样传输数据?
客户端:
1、链接:connect,连接到服务器Accept
2、写入:write——服务器read 往服务器发送请求/向服务器写入账号密码等
3、读取:read——服务器write 客户端读取服务器发来的请求,根据发来的信息进行下一步操作
4、关闭:close——服务器close 如果一直连接socket,会占用资源
服务端:
1、绑定:Bind
2、监听:Listen
ps:1,2步是先把服务器开启起来进行监听,这样客户端才能访问进来
3、创建一个新的链接:Accept。因为服务器不可能只和一个客户端连接,所以和当前客户端建立一个新的连接,当再来别的客户端连接时,再创建新的链接
4、读取:Read 当收到客户端write请求时,读取数据库是否有该账号密码等,进行数据的验证
5、写入:write 当服务器在数据库读取到数据时,服务器返回/发送请求(查询到信息)给客户端
6、关闭:close
注:本段是同步
同步和异步的区别:
同步:只能监听一个
异步:可监听多个(真正根据listdfd.Listen(0); //监听的个数 0代表不限制,同步模式即使这样写也只监听一个)
vs安装:.NET桌面开发
VS新建:控制台应用(.NET Framework) 文件名:mysocket
代码如下:
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace mysocket
{
class Program
{
static void Main(string[] args)
{
Socket listdfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAdr = IPAddress.Parse("127.0.0.1"); //服务器地址
IPEndPoint ipEp = new IPEndPoint(ipAdr, 1234); //服务器地址+端口号(端口号可随意)
listdfd.Bind(ipEp);
listdfd.Listen(0); //监听的个数 0代表不限制
Console.WriteLine("服务器启动成功");
while (true)
{
Socket connfd = listdfd.Accept(); //创建一个新的链接
Console.WriteLine("服务器Accept成功");
byte[] readbuff = new byte[1024];
int count = connfd.Receive(readbuff); //接受客户端信息:有数据发送过来就会接收
string str = Encoding.Default.GetString(readbuff, 0, count);
Byte[] bytes = Encoding.Default.GetBytes("服务器输出" + str);
connfd.Send(bytes);
}
}
}
}
build Settings——Resolution and Presentation——Run in background(勾选),允许你的游戏在后台运行。若取消勾选,则切换到后台时,你的游戏将失去响应
创建connect脚本挂载到Camera,代码如下
using UnityEngine;
using System.Net.Sockets;
public class connect : MonoBehaviour {
Socket socket;
const int buff_size = 1024;
byte[] readbuff = new byte[buff_size];
private void Start()
{
connection();
}
public void connection()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
string host = "127.0.0.1";
int port = 1234;
socket.Connect(host, port);
print("客户端地址" + socket.LocalEndPoint); //显示客户端连接信息
string str = "nihao";
byte[] bytes = System.Text.Encoding.Default.GetBytes(str);
socket.Send(bytes);
int count = socket.Receive(readbuff);
str = System.Text.Encoding.Default.GetString(readbuff, 0, count);
print(str);
socket.Close();
}
}
此时,调试VS,启动Unity,观察到以下结果
在二基础上,打开服务端,添加Conn类,代码如下
using System;
using System.Net.Sockets;
namespace mysocket
{
///
/// 处理客户端链接的类
///
class Conn
{
//定义缓冲区
public const int buff_size = 1024;
//定义服务器
public Socket socket;
//是否使用
public bool isUse = false;
public byte[] readBuff = new byte[buff_size];
public int buffCount = 0;
public Conn()
{
readBuff = new byte[buff_size];
}
//初始化
public void Init(Socket socket)
{
this.socket = socket;
isUse = true;
buffCount = 0;
}
//缓冲所剩余的字节数
public int BuffRemain()
{
return buff_size - buffCount;
}
//获取客户端地址
public string GetAdress()
{
if (!isUse)
return "无法获取地址";
return socket.RemoteEndPoint.ToString();
}
//关闭连接
public void Close()
{
if (!isUse)
return;
Console.WriteLine(GetAdress() + "与服务器断开连接");
socket.Close();
isUse = false;
}
}
}
在第三基础上,同样在服务器端添加Server类,代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace mysocket
{
class Server
{
//监听嵌套字
public Socket listedfd;
//客户端连接
public Conn[] conns;
//最大连接数
public int maxConn = 50;
//获取连接池,返回负数表示连接失败
public int NewIndex()
{
if (conns == null)
return -1; //代表没有连接成功
for(int i = 0; i < conns.Length; i++)
{
if (conns[i] == null)
{
conns[i] = new Conn();
return i;
}
else if (conns[i].isUse == false)
return i;
}
return -1;
}
//开启服务器的方法
public void Start(string host,int port)
{
//连接池
conns = new Conn[maxConn];
for(int i = 0; i < maxConn; i++)
{
conns[i] = new Conn();
}
//Socket
listedfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//Bind
IPAddress ipAdr = IPAddress.Parse(host);
IPEndPoint ipEp = new IPEndPoint(ipAdr, port);
listedfd.Bind(ipEp);
//开启监听
listedfd.Listen(maxConn);
//开启接收数据
listedfd.BeginAccept(AcceptCb, null);
Console.WriteLine("服务器启动成功");
}
//回调,异步接收客户端的数据,循环去实现
void AcceptCb(IAsyncResult ar)
{
try
{
Socket socket = listedfd.EndAccept(ar);
int index = NewIndex();
if (index < 0)
{
socket.Close();
Console.WriteLine("警告连接已满");
}
else
{
Conn conn = conns[index];
conn.Init(socket);
string adr = conn.GetAdress();
Console.WriteLine("有客户端连接" + adr + "ID号为" + index);
conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);
//再次调用,实现循环
listedfd.BeginAccept(AcceptCb, null);
}
}
catch(Exception ex)
{
Console.WriteLine("回调失败" + ex.Message);
}
}
void ReceiveCb(IAsyncResult ar)
{
//异步的状态
Conn conn = (Conn)ar.AsyncState;
try
{
int count = conn.socket.EndReceive(ar);
if (count <= 0)
{
Console.WriteLine(conn.GetAdress() + "与服务器断开连接");
conn.Close();
return;
}
string str = Encoding.Default.GetString(conn.readBuff, 0, count);
Console.WriteLine("收到" + conn.GetAdress() + "发送过来的数据" + str);
str = conn.GetAdress() + ":" + str;
byte[] bytes = Encoding.Default.GetBytes(str);
//广播消息:把这个客户端发过来的消息转发给其他客户端
for(int i = 0;i
将Program的Main()内方法删掉,改为如下异步方法(原内容为同步)
using System;
namespace mysocket
{
class Program
{
static void Main(string[] args)
{
Server server = new Server();
server.Start("127.0.0.1", 1234);
while (true)
{
string str = Console.ReadLine();
switch (str)
{
case "quit": //关闭socket
return;
}
}
}
}
}
在Main界面调试,若出现“服务器启动成功”,则代表成功
基于以上内容,打开Unity的客户端,将connect脚本内容改为如下所示:
using UnityEngine;
using System.Net.Sockets;
using UnityEngine.UI;
using System;
public class connect : MonoBehaviour {
//服务器地址、服务器端口、聊天输入框
public InputField hostInput, portInput,TextInput;
//客户端收到的信息
public Text connecttext;
public Text txtstr; //用于UI显示服务器发送过来的数据
public string serverstr; //注:必须为public!服务器发送回来的数据
//Socket
Socket socket;
const int buff_size = 1024;
public byte[] readbuff = new byte[buff_size];
private void Update()
{
txtstr.text = serverstr;
}
//连接服务器,绑定“连接”按钮
public void Connection()
{
txtstr.text = "";
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//开始连接服务器
string host = hostInput.text;
int port = int.Parse(portInput.text); //转化为int
socket.Connect(host, port);
print("客户端地址" + socket.LocalEndPoint); //输出:客户端地址127.0.0.1:54000
connecttext.text = socket.LocalEndPoint.ToString();
//开始接收数据
//public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socket_flags, AsyncCallback callback, object state);
socket.BeginReceive(readbuff, 0, buff_size, SocketFlags.None, ReceiveCb, null);
}
//回调方法
void ReceiveCb(IAsyncResult ar)
{
try
{
//接收数据的大小
int count = socket.EndReceive(ar);
string str = System.Text.Encoding.Default.GetString(readbuff, 0, count);
if (serverstr.Length > 300) serverstr = "";
serverstr += str + "\n"; //服务器发送回来的数据(经过加换行处理,且公用放在外面)+=接收数据+ "\n"
//继续接收数据
socket.BeginReceive(readbuff, 0, buff_size, SocketFlags.None, ReceiveCb, null);
}
catch(Exception ex)
{
txtstr.text = "连接已经断开" + ex.Message;
socket.Close();
}
}
public void Send()
{
string str = TextInput.text;
byte[] bytes = System.Text.Encoding.Default.GetBytes(str);
try
{
socket.Send(bytes);
}
catch
{
}
}
}
Unity界面如下
现在运行Unity,输入127.0.0.1,端口1234,即可开始通讯
unity发布exe,运行多个exe,可在多个客户端之间进行实时通讯
enjoy~