服务器端与一个客户端连接
与一个客户端连接,客户端只能发送一条消息,下面是简单demo的图示。
测试示图
服务端和客户端在平台是不能同时运行
的,调试的时候我是先把TCPClient发布成了exe,这里需要注意一下,不管是Rider还是VS都只能发布成exe
,由于我是mac本想发布成app,但是尝试了方法很多都是不行,因为.net是针对Windows平台。
mac需要使用mono,服务器端我在Rider平台直接跑,exe则从终端打开,使用mono TCPCLient.exe
。
下图测试成功,客户端先收到“hello client!你好…” 之后输入 “你也好 服务器”发送给服务器,如图,服务器这边也成功收到了消息。
服务器端和客户端的ip可以写成本机127.0.0.1,或者本地IP,如果是局域网的话,ip并不是固定的可能会在某一周期内更新ip,需要随时间修改。此外,申请的端口号必须在1024~63365之间。
服务器
TCPServer
using System;
using System.Net.Sockets;
using System.Net;
// use class Socket and TCP
namespace TCPServer
{
internal class Program
{
public static void Main(string[] args)
{
// 地址类型;socket类型:Dgram(UDP报文) TCP流Stream;协议
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 申请端口,绑定局域网ip 10.8.215.46(变动的) 127.0.0.1本机
// IPAddress ipAddress= new IPAddress(new byte[]{10,8,215,46}); // 把ip构造成数组传递过去
IPAddress ipAddress = IPAddress.Parse("xx.xx.xx.xx"); // Parse把一个ip字符串转换成对象传递
// IpAdress xx.xx.xx.xx IpEedPoint IP地址+终端号
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 4869);
// 绑定ip和端口号
serverSocket.Bind(ipEndPoint);
// 开始监听端口 设置50防止服务器崩溃 设置0表示不限制数量
serverSocket.Listen(0); // 传递挂起的连接队列的最大长度
// 接收客户端信息 程序会暂停,直到有一个客户端连接过来才会继续向下运行
Socket clientSocket = serverSocket.Accept(); // 接收一个客户端连接 返回一个Socket用来跟客户端进行通信
// 向客户端发送一条数据
string msg = "Hello client!你好...";
byte[] data = System.Text.Encoding.UTF8.GetBytes(msg); // 按照utf8将一个字符串转换成byte数组
clientSocket.Send(data); // Send传递byte数组,需要将字符串转换成byte数组 需要使用支持中文的编码
// 接收客户端一条消息
byte[] dataBuffer = new byte[1024]; // 接收时先定义一个数组
int count = clientSocket.Receive(dataBuffer); // 知道数组中前count个是接收到的数据
string msgReceive = System.Text.Encoding.UTF8.GetString(dataBuffer, 0, count); // 转换成字符串
Console.WriteLine(msgReceive);
Console.ReadKey(); // 程序终止的太快,方便观察输出
// 关闭服务器端
clientSocket.Close(); // 关闭与客户端的连接
serverSocket.Close(); // 关闭服务器自身的连接
}
}
}
客户端
TCPClient
using System;
using System.Net.Sockets;
using System.Net;
namespace TCPClient
{
internal class Program
{
public static void Main(string[] args)
{
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 客户端不需要绑定ip和端口 只需要和服务器端建立连接
clientSocket.Connect(new IPEndPoint(IPAddress.Parse("xx.xx.xx.xx"), 4869));
// 服务器会向客户端发送消息,需要接收消息
byte[] data = new byte[1024]; // 接收时先定义一个数组
int count = clientSocket.Receive(data); // 知道数组中前count个是接收到的数据
// 调用玩Receive后整个程序会暂停在这里,直到当它接收到服务器信息的时候
string msg = System.Text.Encoding.UTF8.GetString(data, 0, count); // 转换成字符串
Console.WriteLine(msg);
string str = Console.ReadLine(); // 读取用户一行输入传送给服务器端
clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(str));
Console.ReadKey(); // 程序终止的太快,方便观察输出
clientSocket.Close();
}
}
}
服务器与多客户端的连接
目前客户端只能发送一条数据,让客户端数据while(true)
循环实现多条发送,当客户端输入c,使它正常关闭。
// 数据循环发送
while (true)
{
string str = Console.ReadLine(); // 读取用户一行输入传送给服务器端
// 输入c关闭
if (str =="c")
{
clientSocket.Close();
return;
}
clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(str));
}
异步实现服务器多连
使用异步的方式双层递归使服务器能够与多个客户端连接。try catch对客户端非正常关闭进行监控。根据接收到的客户端信息长度为0时关闭对客户端的连接。
if (count == 0)
{
clientSocket.Close();
return;
}
TCPServer
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
// use class Socket and TCP
namespace TCPServer
{
internal class Program
{
public static void Main(string[] args)
{
StartServerAsync();
// 暂停 防止中断
Console.ReadKey();
}
// 异步 必须是静态
static void StartServerAsync()
{
// 地址类型;socket类型:Dgram(UDP报文) TCP流Stream;协议
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 申请端口,绑定局域网ip 10.8.215.46(变动的) 127.0.0.1本机
// IPAddress ipAddress= new IPAddress(new byte[]{10,8,215,46}); // 把ip构造成数组传递过去
IPAddress ipAddress = IPAddress.Parse("10.8.215.46"); // Parse把一个ip字符串转换成对象传递
// IpAdress xx.xx.xx.xx IpEedPoint IP地址+终端号
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 4869);
// 绑定ip和端口号
serverSocket.Bind(ipEndPoint);
// 开始监听端口 设置50防止服务器崩溃 设置0表示不限制数量
serverSocket.Listen(0); // 传递挂起的连接队列的最大长度
// 接收客户端信息 程序会暂停,直到有一个客户端连接过来才会继续向下运行
// Socket clientSocket = serverSocket.Accept(); // 接收一个客户端连接 返回一个Socket用来跟客户端进行通信
serverSocket.BeginAccept(AcceptCallBack,serverSocket);
}
// 回调函数 接收到一个客户端连接的时候,要对客户端发起监听
static void AcceptCallBack(IAsyncResult ar)
{
Socket serverSocket = ar.AsyncState as Socket;
Socket clientSocket = serverSocket.EndAccept(ar);
// 向客户端发送一条数据
string msg = "Hello client!你好...";
byte[] data = System.Text.Encoding.UTF8.GetBytes(msg); // 按照utf8将一个字符串转换成byte数组
clientSocket.Send(data); // Send传递byte数组,需要将字符串转换成byte数组 需要使用支持中文的编码
// 持续不断的接收消息 BeginReceive()开始异步接收数据 偏移从0开始存,1024最大数量,事件方法,
clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);
// 接收完一个客户端之后,重新调用 继续处理下一个客户端连接
serverSocket.BeginAccept(AcceptCallBack,serverSocket);
}
// 需要一个byte数组存储数据,静态方便内部访问
static byte[] dataBuffer = new byte[1024];
// 当服务器端接收到一条消息的时候,就会调用事件方法 如果想再去处理,传递数据的时候可以通过null进行传递
static void ReceiveCallBack(IAsyncResult ar)
{
// 使用try catch处理客户端异常关闭终端
// 调用完BeginReceive,表示开始监听客户端数据的传递,当它就收到一条客户端数据的时候才会去调用
// 把clientSocket当成一个参数传递到回调方法里面,通过ar.AsyncState来获取到Socket
Socket clientSocket = null;
try
{
clientSocket = ar.AsyncState as Socket;
int count = clientSocket.EndReceive(ar); // 完成数据的接收 count代表接收了多少个字节的数据
// 如果没有接收到数据就关闭
if (count == 0)
{
clientSocket.Close();
return;
}
// utf-8进行反编码
string msg = Encoding.UTF8.GetString(dataBuffer, 0, count);
Console.WriteLine("从客户端接收到数据:" + msg);
// 完成回调 一直接收数据
clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);
}
catch (Exception e)
{
Console.WriteLine(e);
// 只出异常时关闭连接
if (clientSocket != null)
{
clientSocket.Close();
}
}
}
}
}
TCPClient
using System;
using System.Net.Sockets;
using System.Net;
namespace TCPClient
{
internal class Program
{
public static void Main(string[] args)
{
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 客户端不需要绑定ip和端口 只需要和服务器端建立连接
clientSocket.Connect(new IPEndPoint(IPAddress.Parse("10.8.215.46"), 4869));
// 服务器会向客户端发送消息,需要接收消息
byte[] data = new byte[1024]; // 接收时先定义一个数组
int count = clientSocket.Receive(data); // 知道数组中前count个是接收到的数据
// 调用玩Receive后整个程序会暂停在这里,直到当它接收到服务器信息的时候
string msg = System.Text.Encoding.UTF8.GetString(data, 0, count); // 转换成字符串
Console.WriteLine(msg);
// 数据循环发送
while (true)
{
string str = Console.ReadLine(); // 读取用户一行输入传送给服务器端
// 输入c关闭
if (str =="c")
{
clientSocket.Close();
return;
}
clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(str));
}
// Console.ReadKey(); // 程序终止的太快,方便观察输出
// clientSocket.Close();
}
}
}
测试效果
如图所示,每一个客户端都收到了来自服务器的消息,说明三个客户端都能与服务器建立正常的连接。
多个客户端都可以连续发送多条消息往服务器,服务器可以正常接收。
关闭客户端k3之后,服务器可以正常接收k2与k1的消息。在这里客户端k3属于正常关闭。
下面对客户端k2进行异常终止,在这之后,服务器正常接收客户端k1发送的消息,说明服务器与多个客户端之间的互联是正常的。