欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c#Socket服务器与客户端的开发(2)

程序员文章站 2022-11-27 17:06:15
上一篇文章我们使用原生的socket分别实现了服务器和客户端, 本篇文章使用SuperSocket来开发实现服务器, 之前也介绍了SuperSocket是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架。你无须了解如何使用 Socket, 如何维护 Socket 连 ......

上一篇文章我们使用原生的socket分别实现了服务器和客户端,

本篇文章使用supersocket来开发实现服务器,

之前也介绍了supersocket是一个轻量级, 跨平台而且可扩展的 .net/mono socket 服务器程序框架。你无须了解如何使用 socket, 如何维护 socket 连接和 socket 如何工作,但是你却可以使用 supersocket 很容易的开发出一款 socket 服务器端软件,例如游戏服务器,gps 服务器, 工业控制服务和数据采集服务器等等。

接下来开始我们的开发,首先我们需要安装supersocket相关程序包,我们新建一个项目开发supersocket服务器

然后打开nuget程序包管理器,搜索supersocket ,下载安装supersocket和supersocket.engine

c#Socket服务器与客户端的开发(2)

 下载安装完毕后,我们的项目中会自动引用了supersocke和log4net 相关程序集和配置文件

c#Socket服务器与客户端的开发(2)

进入正题上代码,我们这里只用supersocket做服务器端,客户端使用sockettool做测试

 sockettool

链接:https://pan.baidu.com/s/1ykeofuizke2yhe3mmyrbjw
提取码:m2nk

supersocket实现服务器:

  1 using system;
  2 using system.collections.generic;
  3 using system.componentmodel;
  4 using system.data;
  5 using system.drawing;
  6 using system.linq;
  7 using system.text;
  8 using system.threading.tasks;
  9 using system.windows.forms;
 10 using system.net.sockets;
 11 using system.net;
 12 using system.threading;
 13 using supersocket;
 14 using supersocket.socketbase;
 15 using supersocket.socketbase.protocol;
 16 
 17 namespace supersocket
 18 {
 19     public partial class supersocketserver : form
 20     {
 21         public supersocketserver()
 22         {
 23             initializecomponent();
 24         }
 25 
 26         private void supersocketserver_load(object sender, eventargs e)
 27         {
 28             //txt_ip.text = "127.0.0.1";
 29             txt_port.text = "3333";
 30         } 
 31 
 32         //appserver 代表了监听客户端连接,承载tcp连接的服务器实例。理想情况下,我们可以通过appserver实例获取任何你想要的客户端连接,服务器级别的操作和逻辑应该定义在此类之中。
 33         appserver appserver;
 34         //缓冲字节数组
 35         byte[] buffer = new byte[2048];
 36 
 37         string ipaddress_connect;
 38         string ipaddress_close;
 39         string ipaddress_receive;
 40 
 41         //存储session和对应ip端口号的泛型集合
 42         dictionary<string, appsession> sessionlist = new dictionary<string, appsession>();
 43 
 44         enum operatetype
 45         {
 46 
 47             add = 1,  //添加
 48             remove = 2  //移除
 49         }
 50 
 51         /// <summary>
 52         /// 开启服务
 53         /// </summary>
 54         /// <param name="sender"></param>
 55         /// <param name="e"></param>
 56         private void btn_startlisten_click(object sender, eventargs e)
 57         {
 58             appserver = new appserver();
 59             if (!appserver.setup(int.parse(txt_port.text)))
 60             {
 61                 setmessage("failed to setup");
 62                 return;
 63             }
 64             if (!appserver.start())
 65             {
 66                 setmessage("failed to start");
 67                 return;
 68             }
 69             else
 70             {
 71                 setmessage("开启监听");
 72             }
 73             //supersocket自定义了三个事件 ,连接事件,接收事件,关闭事件
 74             appserver.newsessionconnected += appserver_newsessionconnected;
 75             appserver.newrequestreceived += appserver_newrequestreceived;
 76             appserver.sessionclosed += appserver_sessionclosed;
 77         }
 78 
 79         /// <summary>
 80         /// 接收连接
 81         /// </summary>
 82         /// <param name="session"></param>
 83         void appserver_newsessionconnected(appsession session)
 84         {
 85             //有新连接的时候,添加记录  session.localendpoint属性获取当前session的ip和端口号
 86             //appsession 代表一个和客户端的逻辑连接,基于连接的操作应该定于在该类之中。你可以用该类的实例发送数据到客户端,接收客户端发送的数据或者关闭连接。
 87 
 88             //获取远程客户端的ip端口号
 89             ipaddress_connect = session.remoteendpoint.tostring();
 90             comboboxhandle(ipaddress_connect, operatetype.add);
 91             sessionlist.add(ipaddress_connect, session);
 92             setmessage(ipaddress_connect + "已连接!");
 93         }
 94 
 95         /// <summary>
 96         /// 接收数据
 97         /// </summary>
 98         /// <param name="session"></param>
 99         /// <param name="requestinfo"></param>
100         void appserver_newrequestreceived(appsession session, stringrequestinfo requestinfo)
101         {
102             //requestinfo.key 是请求的命令行用空格分隔开的第一部分
103             //requestinfo.parameters 是用空格分隔开的其余部分
104             //requestinfo.body 是出了请求头之外的所有内容
105             ipaddress_receive = session.remoteendpoint.tostring();
106             setmessage("收到" + ipaddress_receive + "数据: "+requestinfo.key +" "+ requestinfo.body);
107         }
108 
109         /// <summary>
110         /// 关闭连接
111         /// </summary>
112         /// <param name="session"></param>
113         /// <param name="value"></param>
114         void appserver_sessionclosed(appsession session, socketbase.closereason value)
115         {   
116             ipaddress_close = session.remoteendpoint.tostring();
117             comboboxhandle(ipaddress_close, operatetype.remove);
118             sessionlist.remove(ipaddress_close);
119             setmessage(ipaddress_close + "已关闭连接!");
120         }
121         /// <summary>
122         /// 发送数据
123         /// </summary>
124         /// <param name="sender"></param>
125         /// <param name="e"></param>
126         private void btn_send_click(object sender, eventargs e)
127         {
128             //从客户端列获取想要发送数据的客户端的ip和端口号,然后从sessionlist中获取对应session然后调用send()发送数据
129             if (cmb_socketlist.items.count != 0)
130             {
131                 if (cmb_socketlist.selecteditem == null)
132                 {
133                     messagebox.show("请选择一个客户端发送数据!");
134                     return;
135                 }
136                 else
137                 {
138                     sessionlist[cmb_socketlist.selecteditem.tostring()].send(txt_send.text);
139                 }
140             }
141             else
142             {
143                 setmessage("当前没有正在连接的客户端!");
144             }
145             txt_send.clear();
146         }
147 
148         /// <summary>
149         /// 添加信息
150         /// </summary>
151         /// <param name="str"></param>
152         private void setmessage(string str)
153         {
154             richtextbox1.invoke(new action(() => { richtextbox1.appendtext(str + "\r\n"); }));
155         }
156 
157         /// <summary>
158         /// combobox操作
159         /// </summary>
160         /// <param name="ipaddress"></param>
161         /// <param name="operatetype">add 添加项/remove 移除项</param>
162         private void comboboxhandle(string ipaddress, operatetype operatetype)
163         {
164             if (operatetype == operatetype.add)
165             {
166                 cmb_socketlist.invoke(new action(() => { cmb_socketlist.items.add(ipaddress); }));
167             }
168             if (operatetype == operatetype.remove)
169             {
170                 cmb_socketlist.invoke(new action(() => { cmb_socketlist.items.remove(ipaddress); }));
171             }
172         }
173         
174     }
175 }

先挂官方说明文档 http://docs.supersocket.net/v1-6/zh-cn

这里说明几点:

  (1)这里appserver_newrequestreceived(appsession session, stringrequestinfo requestinfo)方法中的stringrequestinfo
是包含请求信息的,

  requestinfo.key 是请求的命令行用空格分隔开的第一部分             

  requestinfo.parameters 是用空格分隔开的其余部分,用空格分割开的字符串数组           

  requestinfo.body 是出了请求头之外的所有内容,是一个字符串

  (2)这里requestinfo是客户端发送过来 严格按照 请求头 请求参数 请求参数 请求参数 \r\n 的格式发送, 空格隔开的第一部分是请求头,后边用空格分割后组成的数据就是请求参数

而且必须是以回车换行结尾 supersocket才能正确接收;

  (3)这里请求头和请求参数用什么分割是可以自定义;我们可以自定义appserver类,继承appserver类,然后使用下面的代码扩展命令行协议

  比如用":"分割请求头和请求参数,用","分隔请求参数.

1 public class yourserver : appserver<yoursession>
2 {
3     public yourserver()
4         : base(new commandlinereceivefilterfactory(encoding.default, new basicrequestinfoparser(":", ",")))
5     {
6 
7     }
8 }

接下来我们开始测试,还是默认使用3333端口,开启监听,我们依旧是使用sockettool工具创建三个客户端,一起访问服务器

服务器:

c#Socket服务器与客户端的开发(2)

客户端

c#Socket服务器与客户端的开发(2)

接下来三个客户端分别以"9100"为请求头,test为请求体给服务器发送数据,记住客户端发送数据一定以回车换行为结尾

客户端:

c#Socket服务器与客户端的开发(2)

服务器:

c#Socket服务器与客户端的开发(2)

接下里测试服务器给客户端,这里以服务器给端口为1083的客户端发送数据"aaaa"

从客户端列选择端口号为1083的客户端,在textbox输入aaaa 发送数据

服务器

c#Socket服务器与客户端的开发(2)

客户端

c#Socket服务器与客户端的开发(2)

接下里客户端关闭连接

服务器

c#Socket服务器与客户端的开发(2)

到此,supersocket实现的服务器测试完美收官,其实supersocket的功能远不止于此,我也只是刚开始使用

待后续研究官方文档后什么新的发现在更新,告辞!

两篇文章的源码

本来想上传github的,毕竟这样显得专业一点,奈何初来乍到的,实在操作不了(留下了不懂英文的泪水),还是放云盘吧!

链接:https://pan.baidu.com/s/1zjcvkp2ne9u3kr8vybkhfw
提取码:gee7