C# Socket

目录

目录
异步原理
主要方法
源码
Server源码:
Client源码
实验效果(广播为例)
参考博客

TOC

异步原理

套接字编程原理:延续文件作用思想,打开-读写-关闭的模式。
C/S编程模式如下:
Ø 服务器端:
打开通信通道,告诉本地机器,愿意在该通道上接受客户请求——监听,等待客户请求——接受请求,创建专用链接进行读写——处理完毕,关闭专用链接——关闭通信通道(当然其中监听到关闭专用链接可以重复循环)
Ø 客户端:打开通信通道,连接服务器——数据交互——关闭信道。
Socket通信方式:
Ø 同步:客户端在发送请求之后必须等到服务器回应之后才可以发送下一条请求。串行运行
Ø 异步:客户端请求之后,不必等到服务器回应之后就可以发送下一条请求。并行运行
套接字模式:
Ø 阻塞:执行此套接字调用时,所有调用函数只有在得到返回结果之后才会返回。在调用结果返回之前,当前进程会被挂起。即此套接字一直被阻塞在网络调用上。
Ø 非阻塞:执行此套接字调用时,调用函数即使得不到得到返回结果也会返回。
套接字工作步骤:
Ø 服务器监听:监听时服务器端套接字并不定位具体客户端套接字,而是处于等待链接的状态,实时监控网络状态
Ø 客户端链接:客户端发出链接请求,要连接的目标是服务器端的套接字。为此客户端套接字必须描述服务器端套接字的服务器地址与端口号。
Ø 链接确认:是指服务器端套接字监听到客户端套接字的链接请求时,它响应客户端链接请求,建立一个新的线程,把服务器端套接字的描述发送给客户端,一旦客户端确认此描述,则链接建立好。而服务器端的套接字继续处于监听状态,继续接受其他客户端套接字请求。

主要方法



源码

Server源码:

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Net; 5using System.Net.Sockets; 6using System.Text; 7using System.Threading; 8using System.Threading.Tasks; 9 10namespace ConsoleApp1 11{ 12 public class Conn 13 { //定义数据最大长度 14 public const int data = 1024; 15 //Socket 16 public Socket socket; 17 //是否使用 18 public bool isUse = false; 19 //Buff 20 public byte[] readBuff = new byte[data]; 21 public int buffCount = 0; 22 //构造函数 23 public Conn() 24 { 25 readBuff = new byte[data]; 26 } 27 //初始化 28 public void Init(Socket socket) 29 { 30 this.socket = socket; 31 isUse = true; 32 buffCount = 0; 33 } 34 //缓冲区剩余的字节数 35 public int BuffRemain() 36 { 37 return data - buffCount; 38 } 39 40 //获取客户端地址 41 public string GetAdress() 42 { 43 if (!isUse) 44 return "无法获取地址"; 45 return socket.RemoteEndPoint.ToString(); 46 } 47 //关闭 48 public void Close() 49 { 50 if (!isUse) 51 return; 52 Console.WriteLine("[断开链接]" + GetAdress()); 53 socket.Close(); 54 isUse = false; 55 } 56 } 57 public class Program 58 { 59 /// <summary> 60 /// 创建多个Conn管理客户端的连接 61 /// </summary> 62 public static Conn[] conns; 63 64 /// <summary> 65 /// 最大连接数 66 /// </summary> 67 public static int maxConn = 50; 68 69 /// <summary> 70 /// 将Socket定义为全局变量 71 /// </summary> 72 private static Socket serverSocket; 73 74 /// <summary> 75 /// 获取链接池索引,返回负数表示获取失败 76 /// </summary> 77 /// <returns></returns> 78 public static int NewIndex() 79 { 80 if (conns == null) 81 return -1; 82 for (int i = 0; i < conns.Length; i++) 83 { 84 if (conns[i] == null) 85 86 { 87 conns[i] = new Conn(); 88 return i; 89 } 90 else if (conns[i].isUse == false) 91 { 92 return i; 93 } 94 } 95 return -1; 96 } 97 static void Main(string[] args) 98 { 99 //创建多个链接池,表示创建maxConn最大客户端 100 conns = new Conn[maxConn]; 101 for (int i = 0; i < maxConn; i++) 102 { 103 conns[i] = new Conn(); 104 } 105 serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 106 IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000); 107 serverSocket.Bind(ipEndPoint);//绑定IP和端口号 108 serverSocket.Listen(maxConn);//开始监听端口,0为监听无限个客户端 109 Console.WriteLine("[服务器]启动成功"); 110 //开始调用异步连接 111 serverSocket.BeginAccept(AcceptCb, null); 112 //按下quit退出程序 113 while (true) 114 { 115 if (Console.ReadLine() == "quit") return; 116 } 117 } 118 /// <summary> 119 /// Accept回调 120 /// </summary> 121 /// <param name="ar"></param> 122 static void AcceptCb(IAsyncResult ar) 123 { 124 try 125 { 126 Socket socket = serverSocket.EndAccept(ar);//尝试进行异步连接 127 int index = NewIndex(); 128 if (index < 0) 129 { 130 socket.Close(); 131 Console.Write("[警告]链接已满"); 132 } 133 else 134 { 135 Conn conn = conns[index]; 136 conn.Init(socket); 137 string adr = conn.GetAdress(); 138 Console.WriteLine("客户端连接 [" + adr + "] conn池ID:" + index); 139 conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn); 140 } 141 serverSocket.BeginAccept(AcceptCb, null); 142 } 143 catch (Exception e) 144 { 145 Console.WriteLine("AcceptCb失败:" + e.Message); 146 } 147 } 148 /// <summary> 149 /// 接收回调 150 /// </summary> 151 /// <param name="ar"></param> 152 static void ReceiveCb(IAsyncResult ar) 153 { 154 Conn conn = (Conn)ar.AsyncState; 155 try 156 { 157 int count = conn.socket.EndReceive(ar); 158 //关闭信号 159 if (count <= 0) 160 { 161 Console.WriteLine("收到 [" + conn.GetAdress() + "] 断开链接"); 162 conn.Close(); 163 return; 164 } 165 //数据处理 166 string str = Encoding.UTF8.GetString(conn.readBuff, 0, count); 167 Console.WriteLine("收到 [" + conn.GetAdress() + "] 数据:" + str); 168 str = conn.GetAdress() + "发送的:" + str; 169 byte[] bytes = System.Text.Encoding.UTF8.GetBytes("接收到" + str); 170 //广播 171 /* 172 for (int i = 0; i < conns.Length; i++) 173 { 174 if (conns[i] == null) 175 continue; 176 if (!conns[i].isUse) 177 continue; 178 Console.WriteLine("将消息转播给 " + conns[i].GetAdress()); 179 conns[i].socket.Send(bytes); 180 }*/ 181 //点播 182 for (int i = 0; i<=0;i++) 183 { 184 if (conns[i] == null) 185 continue; 186 if (!conns[i].isUse) 187 continue; 188 Console.WriteLine("将消息转播给 " + conns[i].GetAdress()); 189 conns[i].socket.Send(bytes); 190 } 191 //继续接收 192 conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(),SocketFlags.None, ReceiveCb, conn); 193 } 194 catch (Exception e) 195 { 196 Console.WriteLine("收到 [" + conn.GetAdress() + "] 断开链接"); 197 conn.Close(); 198 } 199 } 200 } 201}

Client源码

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Net; 5using System.Net.Sockets; 6using System.Text; 7using System.Threading; 8using System.Threading.Tasks; 9 10 11namespace ConsoleApp1 12{ 13 public class Program 14 { 15 private static int datacount = 1024;//设置数组数据长度 16 private static Socket socket; 17 private static byte[] dataBuff = new byte[datacount]; 18 private static string recvStr; 19 static void Main(string[] args) 20 { 21 //获取到Socket协议 22 socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 23 //绑定IP与端口 24 IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000); 25 socket.Connect(ipEndPoint);//与服务端进行连接 26 Console.WriteLine("客户端地址 " + socket.LocalEndPoint.ToString());//获取客户端地址 27 //开启异步接收模式 28 socket.BeginReceive(dataBuff, 0, datacount, SocketFlags.None, ReceiveCb, null); 29 socket.Send(Encoding.UTF8.GetBytes("123")); //封装好的一个Send方法,能够在方法中操作,比如说:释放资源 30 Send(socket, "456"); //简单的发送 31 while (true) 32 { 33 //向服务器发送数据 34 byte[] data = Encoding.UTF8.GetBytes(Console.ReadLine());//输入字符 35 //按下回车键发送数据 36 ConsoleKey inpt = Console.ReadKey().Key; 37 if (inpt == ConsoleKey.Enter) 38 { 39 socket.Send(data); 40 Console.WriteLine("发送成功"); 41 } 42 } 43 } 44 private static void Send(Socket handler, String data) 45 { 46 byte[] byteData = Encoding.ASCII.GetBytes(data); 47 handler.BeginSend(byteData, 0, byteData.Length, 0, 48 new AsyncCallback(SendCallback), handler); 49 } 50 private static void SendCallback(IAsyncResult ar) 51 { 52 try 53 { 54 Socket handler = (Socket)ar.AsyncState; 55 int bytesSent = handler.EndSend(ar); 56 Console.WriteLine("Sent {0} bytes to client.", bytesSent); 57 } 58 catch (Exception e) 59 { 60 Console.WriteLine(e.ToString()); 61 } 62 } 63 private static void ReceiveCb(IAsyncResult ar) 64 { 65 //接收数据的长度 66 //数据处理 67 //将dataBuffer数组转码成字符的形式输出 68 //dataBuffer:需要转码的数组 69 //0:从数组的长度,0开始读取。 70 //count :读取数组的最大长度 71 int count = socket.EndReceive(ar); 72 if(count>0) //当接受数据长度大于0时处理 73 { 74 string str = System.Text.Encoding.UTF8.GetString(dataBuff, 0, count); 75 //获取服务器端的数据 76 Console.WriteLine("获取服务端的数据:" + str); 77 //继续接收 78 socket.BeginReceive(dataBuff, 0, datacount, SocketFlags.None, ReceiveCb, null); 79 } 80 } 81 } 82}

实验效果(广播为例)

参考博客

C# Socket编程 同步及异步通信:https://blog.csdn.net/bemodesty/article/details/84396658
C# Socket之异步连接(一):https://blog.csdn.net/u010511043/article/details/86435701
C# Socket之异步连接(二):https://blog.csdn.net/u010511043/article/details/86437192
C# Socket之客户端异步连接:https://blog.csdn.net/u010511043/article/details/86441724

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java修道之路,问鼎巅峰,我辈代码修仙法力齐天

<center<fontcolor00FF7Fsize5face"黑体"代码尽头谁为峰,一见秃头道成空。</font<center<fontcolor00FF00size5face"黑体"编程修真路破折,一步一劫渡飞升。</font众所周知,编程修真有八大境界:1.Javase练气筑基2.数据库结丹3.web前端元婴4.Jav