C# SuperSocket 手把手教你入门 傻瓜教程---4(创建一个最简单的服务器和多客户端双向通信程序)-程序员宅基地

技术标签: C#  c#  服务器和多客户端双向通信  手把手教你入门  傻瓜教程  SuperSocket  

  C# SuperSocket 手把手教你入门 傻瓜教程系列教程

C# SuperSocket 手把手教你入门 傻瓜教程---1(服务器单向接收客户端发送数据)

C# SuperSocket 手把手教你入门 傻瓜教程---2(服务器和客户端双向通信)

C# SuperSocket 手把手教你入门 傻瓜教程---3(Telnet服务器和客户端请求处理)

C# SuperSocket 手把手教你入门 傻瓜教程---4(创建一个服务器和多客户端双向通信程序)

C# SuperSocket 手把手教你入门 傻瓜教程---5(探索自定义AppServer、AppSession,Conmmand,用配置文件App.comfig启动服务器)

C# SuperSocket 手把手教你入门 傻瓜教程---6(CommandLineProtocol---命令行协议)

C# SuperSocket 手把手教你入门 傻瓜教程---7(自定义CommandLineProtocol---命令行协议)

C# SuperSocket 手把手教你入门 傻瓜教程-8(TerminatorReceiveFilter - 结束符协议)

目录

一、新建一个SuperSocket服务器项目

二、安装SuperSocket和SuperSocket.Engine

三、编写程序代码

四、验证


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

         接下来开始我们的开发,首先我们需要安装SuperSocket相关程序包,我们新建一个项目开发SuperSocket服务器,然后打开NuGet程序包管理器,搜索SuperSocket ,下载安装SuperSocketSuperSocket.Engine

一、新建一个SuperSocket服务器项目

        新建一个SuperSocket服务器项目(WINDOWS窗体应用),参见下图。

二、安装SuperSocket和SuperSocket.Engine

        安装SuperSocket和SuperSocket.Engine之前,引用中的组件参见下图。

1、进入【管理NuGet程序包】

鼠标右键单击【引用】,弹出下拉菜单,在下拉菜单中选中【管理NuGet程序包(N)】

2、安装SuperSocket

(1)、【浏览】选项卡下面的输入框中输入SuperSocket

(2)、过一会下面会出现"SuperSocket 由Kerry Jiang,285K个下载 v1.6.6.1"

(3)、鼠标点击"SuperSocket 由Kerry Jiang,285K个下载 v1.6.6.1",右边会出现"版本:最新稳定版1.6.6.1"

(4)、点击"版本:最新稳定版1.6.6.1"右边的【安装】按钮

弹出“预览更改”窗口,点击【确定】按钮

弹出“接受许可”窗口,点击【我接受】按钮

        然后开始安装 SuperSocket,安装完毕,参见右边引用中的组件如下图所示,可以观察到增加了4个组件:

             log4net、SuperSocket.Common,SuperSocket.Facility,SuperSocket.SocketBase

3、安装SuperSocket.Engine

(1)、鼠标点击"SuperSocket.Engine 由Kerry Jiang,165K个下载 v1.6.6.1",右边会出现"版本:1.6.6.1"

(2)、点击"版本:1.6.6.1"右边的【安装】按钮

弹出“预览更改”窗口,点击【确定】按钮

弹出“接受许可”窗口,点击【我接受】按钮

        然后开始安装 SuperSocket.Engine,安装完毕,参见右边引用中的组件如下图所示,可以观察到增加了2个组件:

            SuperSocket.SocketEngine,SuperSocket.SocketService。

三、编写程序代码

        1、Form1窗体增加3个label,Name依次命名为label1,label2,label3,标签内容依次为:IP地址,端口、客户端列表。

        2、Form1窗体增加3个TextBox,Name依次为textBox_ip,textBox_port,textBox_send,TEXT内容依次为:127.0.0.1,5000、空。

3、Form1窗体增加2个Button,Name依次为btn_Listen,btn_send,TEXT内容依次为:开启监听,发送数据。 

4、Form1窗体的【客户端列表】右边增加1个ComboBox,Name为comboBox_SocketList。 

4、Form1窗体增加1个RichTextBox,Name为richTextBox1。  

5、Form1.cs中编写如下代码

 完整的源程序如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


using SuperSocket;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Protocol;



namespace SuperSocket
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            textBox_ip.Text = "127.0.0.1";
            textBox_port.Text = "5000";
        }

  

        //----------------------------------------------------------------------
        //AppServer 代表了监听客户端连接,承载TCP连接的服务器实例。
        //理想情况下,我们可以通过AppServer实例获取任何你想要的客户端连接,
        //服务器级别的操作和逻辑应该定义在此类之中。
        //----------------------------------------------------------------------
        AppServer appServer;


        string ipAddress_Connect;
        string ipAddress_Close;
        string ipAddress_Receive;

        //存储session和对应ip端口号的泛型集合
        Dictionary<string, AppSession> sessionList = new Dictionary<string, AppSession>();

        enum OperateType
        {
            Add = 1,    //添加
            Remove = 2  //移除
        }


        private void btn_Listen_Click(object sender, EventArgs e)
        {
            appServer = new AppServer();
            if (!appServer.Setup(int.Parse(textBox_port.Text)))
            {
                richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("Failed to Setup" + "\r\n"); }));
                return;
            }

            if (!appServer.Start())
            {
                richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("Failed to Start" + "\r\n"); }));
                return;
            }
            else
            {
                richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("开启监听" + "\r\n"); }));
            }

            //SuperSocket自定义了三个事件 ,连接事件,接收事件,关闭事件
            appServer.NewSessionConnected += appServer_NewSessionConnected; //连接事件
            appServer.NewRequestReceived += appServer_NewRequestReceived;   //接收事件
            appServer.SessionClosed += appServer_SessionClosed;             //关闭事件
        }


        private void btn_send_Click(object sender, EventArgs e)
        {
            //从客户端列获取想要发送数据的客户端的ip和端口号,然后从sessionList中获取对应session然后调用send()发送数据
            if (comboBox_SocketList.Items.Count != 0)
            {
                if (comboBox_SocketList.SelectedItem == null)
                {
                    MessageBox.Show("请选择一个客户端发送数据!");
                    return;
                }
                else
                {
                    sessionList[comboBox_SocketList.SelectedItem.ToString()].Send(textBox_send.Text);
                }
            }
            else
            {
                richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("当前没有正在连接的客户端!" + "\r\n"); }));
            }
        }


        /// <summary>
        /// 接收连接
        /// </summary>
        /// <param name="session"></param>
        void appServer_NewSessionConnected(AppSession session)
        {
            //有新连接的时候,添加记录  session.LocalEndPoint属性获取当前session的ip和端口号
            //AppSession 代表一个和客户端的逻辑连接,基于连接的操作应该定于在该类之中。你可以用该类的实例发送数据到客户端,接收客户端发送的数据或者关闭连接。

            //获取远程客户端的ip端口号
            ipAddress_Connect = session.RemoteEndPoint.ToString();
            Combobox_Handle_ipAddress(ipAddress_Connect, OperateType.Add);
            sessionList.Add(ipAddress_Connect, session);
            richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(ipAddress_Connect + "已连接!" + "\r\n"); }));
        }


        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="session"></param>
        /// <param name="requestInfo"></param>
        void appServer_NewRequestReceived(AppSession session, StringRequestInfo requestInfo)
        {
            //requestInfo.Key 是请求的命令行用空格分隔开的第一部分
            //requestInfo.Parameters 是用空格分隔开的其余部分
            //requestInfo.Body 是出了请求头之外的所有内容

            ipAddress_Receive = session.RemoteEndPoint.ToString();
            richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText("收到" + ipAddress_Receive + "数据: " + requestInfo.Key + " " + requestInfo.Body + "\r\n"); }));
        }


        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <param name="session"></param>
        /// <param name="value"></param> 
        /// 
        //static void appServer_SessionClosed(AppSession session, CloseReason value)
        void appServer_SessionClosed(AppSession session, SocketBase.CloseReason value)
        {
            ipAddress_Close = session.RemoteEndPoint.ToString();
            Combobox_Handle_ipAddress(ipAddress_Close, OperateType.Remove);
            sessionList.Remove(ipAddress_Close);
            richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(ipAddress_Close + "已关闭连接!" + "\r\n"); }));
        }

        /// <summary>
        /// combobox操作
        /// </summary>
        /// <param name="ipAddress"></param>
        /// <param name="operateType">add 添加项/remove 移除项</param>
        private void Combobox_Handle_ipAddress(string ipAddress, OperateType operateType)
        {
            if (operateType == OperateType.Add)
            {
                comboBox_SocketList.Invoke(new Action(() => { comboBox_SocketList.Items.Add(ipAddress); }));
            }

            if (operateType == OperateType.Remove)
            {
                comboBox_SocketList.Invoke(new Action(() => { comboBox_SocketList.Items.Remove(ipAddress); }));
            }
        }
    }
}

这里说明几点:

(1)、appServer_NewRequestReceived(AppSession session, StringRequestInfo requestInfo)

方法中的StringRequestInfo是包含请求信息的,

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

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

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

(2)、这里requestInfo是客户端发送过来严格按照:请求头 请求参数 请求参数 请求参数 \r\n 的格式发送,空格隔开的第一部分是请求头,后边用空格分割后组成的数据就是请求参数,而且必须是以回车换行结尾 SuperSocket才能正确接收。

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

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

public class YourServer : AppServer<YourSession>
{
    public YourServer()
        : base(new CommandLineReceiveFilterFactory(Encoding.Default, new BasicRequestInfoParser(":", ",")))
    {

    }
}

四、验证

        接下来我们开始测试,默认使用5000端口,开启监听,我们使用SocketTool工具创建二个客户端,一起访问服务器。

        1、SocketTool工具

        2、运行服务器程序

              运行服务器程序,然后点击【开启监听】按钮。

        3、运行第一个客户端程序

        鼠标先点击TCP_Client,然后点击【创建】按钮,弹出【创建Socket】对话框,在【创建Socket】对话框的【对方IP】输入127.0.0.1,在【对方端口】输入5000,然后点击【确定】按钮。

        鼠标在客户端软件(TCP/UDP Socket调试工具V2.2)上点击【连接】按钮,参见下图,服务器监听到客户端,同时服务器和客户端连接成功。

        首先在客户端列表中找到刚刚连接成功的客户端连接,然后服务器给客户端发送字符串"I AM SERVER!" 

        可以观察到客户端收到服务器发送的字符串"I AM SERVER!" 

         客户端给服务器发送字符串"I am Client1"

警告:客户端发送数据一定以回车换行符作为结尾,这是SuperSocket的强制要求。因此客户端输入完字符串"I am Client1"之后,必须再敲回车换行符。

        4、运行第二个客户端程序 

        鼠标先点击TCP_Client,然后点击【创建】按钮,弹出【创建Socket】对话框,在【创建Socket】对话框的【对方IP】输入127.0.0.1,在【对方端口】输入5000,然后点击【确定】按钮。

        鼠标点击【连接】按钮,可以在服务器观察到连接成功。服务器的客户端列表上出现了客户的连接。

         首先在客户端列表中找到刚刚连接成功的客户端连接,然后服务器给客户端发送字符串“Hello Client2!!!”

        可以观察到客户端收到服务器发送的字符串“Hello Client2!!!”

         客户端2给服务器发送字符串“Hello sever I am Client2!!!”

警告:客户端发送数据一定以回车换行符作为结尾,这是SuperSocket的强制要求。因此客户端输入完字符串“Hello sever I am Client2!!!”之后,必须再敲回车换行符。

         可以观察到服务器收到了客户端2发送的字符串。

源程序参见如下链接:

SuperSocket(最简单的服务器和多客户端双向通信程序).zip_supersocket长连接-其它文档类资源-CSDN下载

源程序中包含SocketTool.exe TCP&UPD测试工具

致谢:

黄昏前黎明后

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ba_wang_mao/article/details/115672920

智能推荐

vue2 实现echarts图表进入可视区域后再加载动画,以及 使用了resize之后,动画失效问题解决

是一个现代的浏览器 API,用于监测一个或多个目标元素与其祖先元素或视窗(viewport)之间的交叉状态(intersection)的变化。它可以有效地监听元素是否进入或离开可视区域,从而实现一些懒加载、无限滚动、图表加载等需求。

【Kotlin】Channel简介

Channel 是一个并发安全的阻塞队列,可以通过 send 函数往队列中塞入数据,通过 receive 函数从队列中取出数据。当队列被塞满时,send 函数将被挂起,直到队列有空闲缓存;当队列空闲时,receive 函数将被挂起,直到队列中有新数据存入。Channel 中队列缓存空间的大小需要在创建时指定,如果不指定,缓存空间默认是 0。

ORACLE LINUX 7.9 上安装 Caché数据库_cache数据库下载-程序员宅基地

ORACLE LINUX 7.9 上安装 Caché数据库。该文介绍了在LINUX上安装Caché数据库的步骤,包括下载安装介质和访问链接信息。

Matlab数据处理——数据的保存和读取方法操作_matlab中的 store-程序员宅基地

文章浏览阅读2.3k次。原文链接:https://blog.csdn.net/misayaaaaa/article/details/533964031:dlmwrite()函数保存成txt文件使用方法:dlmwrite('filename', M)使用默认分隔符“,”将矩阵M写入文本文件filename中;dlmwrite('filename', M, 'D')..._matlab中的 store

你的网站还在使用HTTP? 免费升级至HTTPS吧

http升级到https的关键是安装部署ssl证书,本文阐述了从证书申请到启用https的详细过程

关于Kotlin

在数据科学和机器学习领域,Kotlin的强大类型推断能力和函数式编程特性,使得数据处理和算法实现更加简洁和可读。此外,Kotlin还可用于游戏开发,特别是移动游戏开发,以及嵌入式系统的开发。它几乎可以运行在任何Java语言可以运行的地方,但相比Java,Kotlin更加简洁、高效和安全。它不仅可以编译成Java字节码,在Java虚拟机上运行,还可以编译成JavaScript,以便在没有JVM的设备上运行。此外,Kotlin还可以编译成二进制代码,直接运行在机器上,如嵌入式设备或iOS。

随便推点

DateTimePicker控件进行时间选择与显示_vb.net datetimepicker 怎么选时间-程序员宅基地

文章浏览阅读490次。1.格式化选python基础教程择时间Start_Time.Format = DateTimePickerFormat.Custom; Start_Time.CustomFormat = "yyyy-MM-dd-HH:mm";2.时间c#教程比较大小DateTime t1 = DateTime.Parse(Start_Time.Text.Trim());DateTime t2 = DateTime.Parse(End_Time.Text.Trim());if (DateTime.Compare_vb.net datetimepicker 怎么选时间

C++11:shared_ptr循环引用问题

详细介绍了引用循环出现的场景及为什么会出现引用循环,以及如何解决引用循环,引入了weak的概念。

Spring Boot的热部署工具“AND”Swagger测试工具

指的是在项目无需重启的情况下,只需要刷新页面,即可获得已经修改的样式或功能。要注意该工具一般用于开发环境,在生产环境中最好不要添加这个工具。对于无需重启便可刷新这么方便的工具,在项目中该如何使用:在spring boot 项目中使用工具的方法就是引入相关依赖,热部署工具的依赖如下:

SpringIoc的注入原理_springioc注入原理-程序员宅基地

文章浏览阅读589次。SpringBean的注入原理spring是在配置类需要指定扫描包,然后递归得到下面所有的文件;(springboot默认启动类和兄弟目录下面所有的包文件)包名+文件名=类全限定名;calss.from加载到内存当中,得到字节码(class);判断这个类的脑门上是否有注解(就是类的头顶上),有注解的话,就把这个类先put到Map里面(ResourcesMap和autowiredMap各一..._springioc注入原理

ES6模块化使用方法_如何用es6模块化项目-程序员宅基地

文章浏览阅读334次。目录结构a.js对外导出对象let name = 'j'let age = 18let sex = 'm'// 导出对象export { name, age}// 默认导出export default sexb.js// 导入a.js中导出对象import {name, age} from './a.js'// 导入a.js中默认导出import sex from './a.js'console.log(name, age, sex)index_如何用es6模块化项目

高中教学分析系统数据可视化探索【可视化实战案例】_对高中教学系统进行可视化分析,-程序员宅基地

文章浏览阅读809次。教育行业中大数据分析的主要目的包括改善学生成绩、服务教务设计、优化学生服务等。而学生成绩中有一系列重要的信息往往被我们常规研究所忽视。通过大数据分析和可视化展示,挖掘重要信息,改善 学生服务,对于教学改进意义重大。美国教育部门构建“学习分析系统”,旨在向教育工作者提供了解学生到底是在怎样学习的更好、更好、更精确信息。利用大数据的分析学习能够向教育工作者提供有用的信息,从而帮助其回答众多不易回答的现实问题。_对高中教学系统进行可视化分析,