TCP服务器端与多个客户端连接的C#代码实现

服务器端与一个客户端连接

与一个客户端连接,客户端只能发送一条消息,下面是简单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发送的消息,说明服务器与多个客户端之间的互联是正常的。
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wankcn/article/details/107007522