C# Socket教程入门,Socket的使用方法

开源君 / 03-31 / 46点赞 / 0评论 / 865阅读
## 核心: 在服务器端和客户端的两个Socket实例,共同维持一个连接,而我们还需要一个服务端绑定了特定端口号的固定的serverSocket,用来监听远方clientSocket的申请,并为这个申请建立服务端对应的proxSocket > 服务器维护:`serverSocket` | 服务端实例 | 客户端实例 | 关系 | | --- | --- | --- | | proxSocket | clientSocket | 一一对应 | [微软官方文档](https://learn.microsoft.com/zh-cn/dotnet/api/system.net.sockets?view=netcore-3.1) ### 一、要有服务端 #### --配置-- 1、new Socket > > > `Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ` > 2、Bind > > > `ServerSocket.Bind(new IPEndPoint(IPAddress.Parse(txtIP), int.Parse(txtPost))); //服务器绑定本机IP和端口` > 3、Listen > > > `ServerSocket.Listen(10); //最大连接数是10` > 4、Accept > > > `Socket proxSocket = ServerSocket.Accept(); //监听Socket连接行为,并为监听到的链接创建Socket实例,会阻塞线程直到端口链接` > #### --信息发送与传输-- 5、Receive > > > `proxSocket.Receive(data, 0, data.Length, SocketFlags.None); //data是要写入的byte数组,会阻塞线程直到侦听到slientSocket的Send并持续到IO结束` > 6、Send > > > `proxSocket.Send(result, 0, result.Length, SocketFlags.None); //result是要上传的byte数组` > ### 二、还有客户端 #### --配置-- 1、new Socet > > > `ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);` > 2、Connect > > > `ClientSocket.Connect(new IPEndPoint(IPAddress.Parse(txtIP), int.Parse(txtPort))); //尝试链接服务器的相应IP和端口,连接成功后,服务器端创建一个特定的proxSocket用来进行信息交换` > #### --信息发送与传输-- 3 > > > `ClientSocket.Receive(data, 0, data.Length, SocketFlags.None); ` > 4 > > > `ClientSocket.Send(result, 0, result.Length, SocketFlags.None);` > ### 三、简单实现 ```csharp using System; using System.Net; using System.Net.Sockets; using System.Text; namespace SockerDamo { class SocketServer { List ClientProxSocketList = new List();//用于存放客户端链接 Socket ServerSocket { get; set; } public void Start()//创建服务器Socket,开始侦听客户端,并开始循环接收消息 { //1.创建socket对象 Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ServerSocket = serverSocket; //first is 寻址方式(此处是Ipv4) //second is the socket's type //And the thrid is the 传输协议 System.Console.WriteLine("请输入IP地址:"); String txtIP = Console.ReadLine(); System.Console.WriteLine("请输入端口号:"); String txtPost = Console.ReadLine(); //2.绑定端口IP ServerSocket.Bind(new IPEndPoint(IPAddress.Parse(txtIP), int.Parse(txtPost))); //socket's Bind action need a IPEndPoint to bind the socket and the computer's ip and post //so in the IPEndPoint we need a IPAddress and a int number(Post) //3.开始侦听 ServerSocket.Listen(20);//the number 表示 同时排队的最大限制数量 //同时来了100个链接请求,只能处理一个链接,队列里面放20个等待链接的客户端,其他的返回错误消息。 //4.开始接受客户端的链接 //为了不阻塞主线程,我们应该把以下代码放在一个新的线程(池)中 try { System.Console.WriteLine("开始监听链接..."); Socket proxSocket = ServerSocket.Accept();//这个方法会阻塞当前线程,直到监听到链接 AppendTextToTxtLog(string.Format("客户端{0}已链接", proxSocket.RemoteEndPoint.ToString())); byte[] data = new byte[1024 * 1024 * 256];//用来放数据的数组 int len; while (true) { System.Console.WriteLine("请选择模式:"); System.Console.WriteLine("0.等待接收消息"); System.Console.WriteLine("1.发送消息"); string mo = Console.ReadLine(); //获取客户端数据并返回读取的字节数,会阻塞当前线程, //当客户端突然掉线,往往会引发异常 if (mo == "0") { try { len = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);//往data数组里,从index=0开始,最长写入数据长度为data.Length if (data[0] == 0) { StopConnect(proxSocket); }//正常退出逻辑 else if (data[0] == 1) { string str = Encoding.UTF8.GetString(data, 1, len - 1);//在data数组中从0开始,len个数据,转成str AppendTextToTxtLog(string.Format("接收到客户端:{0} 的信息是:{1}", proxSocket.RemoteEndPoint.ToString(), str)); } } catch (Exception) { Console.WriteLine("接收数据出错"); StopConnect(proxSocket); } } else if (mo == "1") { SendMsg(); } } } catch { AppendTextToTxtLog("出问题啦:您在接收客户端的请求的时候,发生了异常,夭寿啦```"); } } private void StopConnect(Socket proxSocket)//停止链接 { try { if (proxSocket.Connected) { proxSocket.Shutdown(SocketShutdown.Both); proxSocket.Close(100); } } catch (Exception ex) { System.Console.WriteLine("或许没有成功的停止连接" + ex.ToString()); } } //发送消息 private void SendMsg() { String txtMsg = Console.ReadLine(); foreach (var proxSocket in ClientProxSocketList) { if (proxSocket.Connected) { byte[] data = Encoding.UTF8.GetBytes(txtMsg);//信息转二进制 byte[] result = new byte[data.Length + 1];//信息的二进制放在1~data.Length result[0] = 1;//第0个位置是标志位,将来我们可以用这个标志位,分辨文件类型,将来用来传文件 Buffer.BlockCopy(data, 0, result, 1, data.Length);//把 data从0 转 result从1 长度data.Length proxSocket.Send(result, 0, result.Length, SocketFlags.None);//发送消息 } } AppendTextToTxtLog(string.Format("{0}(本机) 的信息是:{1}", ServerSocket.LocalEndPoint.ToString(), txtMsg)); } private void AppendTextToTxtLog(string txt) { Console.WriteLine(txt); } } class SocketClient { Socket ClientSocket { get; set; } public void Start()//创建客户端Socket,并开始循环接收消息 { #region 获取服务器端口内容 System.Console.WriteLine("请输入服务器IP:"); String txtIP = Console.ReadLine(); System.Console.WriteLine("请输入端口号:"); String txtPort = Console.ReadLine(); #endregion //客户端链接服务器端 //1.创建Socket对象 ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2.连接服务器端Socket try//如果服务器的Listen队列已满,那么就会返回一个异常 { ClientSocket.Connect(new IPEndPoint(IPAddress.Parse(txtIP), int.Parse(txtPort))); } catch { System.Console.WriteLine("当前服务器无法连接"); //Thread.Sleep(1000); return; } //3.接收服务器端消息 byte[] data = new byte[1024 * 1024 * 256];//传输数据放在这里,这里是256MB = 256 * 1024 KB = 256 * 1024 * 1024Byte int len; string str; string wenJianMing = ""; while (true) { System.Console.WriteLine("请选择模式:"); System.Console.WriteLine("0.等待接收消息"); System.Console.WriteLine("1.发送消息"); string mo = Console.ReadLine(); len = 0; str = null; if (mo == "0") try { len = ClientSocket.Receive(data, 0, data.Length, SocketFlags.None);//获取服务器端数据并返回读取的字节数,会阻塞当前线程, //当客户端突然掉线,往往会引发异常 if (data[0] == 0)//模式0退出 { AppendTextToTxtLog(string.Format("服务端:{0} 正常退出", ClientSocket.RemoteEndPoint.ToString())); StopConnect(); } else if (data[0] == 1) {//模式1收到了字符串 str = Encoding.UTF8.GetString(data, 1, len - 1); AppendTextToTxtLog(string.Format("Socket信息:{0}", str)); } //一次类推,总共有256种消息的传输模式 } catch (Exception) { AppendTextToTxtLog(string.Format("服务端:{0} 非正常退出", ClientSocket.RemoteEndPoint.ToString())); StopConnect(); return; } else if(mo == "1"){ SendMsg(); } } // 也可以将第3步写入方法ReceiveData // Thread thread = new Thread(new ParameterizedThreadStart(ReceiveData)); // thread.IsBackground = true; // thread.Start(); // 然后开启线程 } private void AppendTextToTxtLog(string txt) { Console.WriteLine(txt); } private void StopConnect() { try { if (ClientSocket.Connected) { ClientSocket.Shutdown(SocketShutdown.Both); ClientSocket.Close(100); } } catch (Exception ex) { System.Console.WriteLine("或许没有成功的停止连接" + ex.ToString()); } } public void SendMsg() { if (ClientSocket.Connected) { System.Console.WriteLine("请输入要发送信息的模式:"); System.Console.WriteLine("0:结束"); System.Console.WriteLine("1:字符串"); String ms = Console.ReadLine(); if (ms == "0") { } if (ms == "1") { System.Console.WriteLine("请输入要发送的消息:"); String s = Console.ReadLine(); byte[] data = Encoding.UTF8.GetBytes(s); byte[] result = new byte[data.Length + 1];//信息的二进制放在1~data.Length result[0] = 1;//第0个位置是标志位,将来我们可以用这个标志位,分辨文件类型,将来用来传文件 Buffer.BlockCopy(data, 0, result, 1, data.Length);//把 data从0 转 result从1 长度data.Length ClientSocket.Send(result, 0, result.Length, SocketFlags.None);//发送消息 AppendTextToTxtLog(string.Format("本机的信息是:{0}", s)); } } } } internal class Program { static void Main(string[] args) { System.Console.WriteLine("0:服务器端"); System.Console.WriteLine("1:客户端"); string ms = Console.ReadLine(); if (ms == "0") { SocketServer server = new SocketServer(); server.Start(); } else if (ms == "1") { SocketClient client = new SocketClient(); client.Start(); } } } } ```