技术标签: C# 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.Engine
SuperSocket是一个轻量级,跨平台而且可扩展的Net/Mono Socket服务器程序框架。你无须了解如何使用 Socket,如何维护Socket连接和Socket如何工作,但是你却可以使用SuperSocket很容易的开发出一款Socket服务器端软件,例如游戏服务器,GPS 服务器, 工业控制服务和数据采集服务器等等。
接下来开始我们的开发,首先我们需要安装SuperSocket相关程序包,我们新建一个项目开发SuperSocket服务器,然后打开NuGet程序包管理器,搜索SuperSocket ,下载安装SuperSocket和SuperSocket.Engine。
新建一个SuperSocket服务器项目(WINDOWS窗体应用),参见下图。
安装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测试工具
致谢:
是一个现代的浏览器 API,用于监测一个或多个目标元素与其祖先元素或视窗(viewport)之间的交叉状态(intersection)的变化。它可以有效地监听元素是否进入或离开可视区域,从而实现一些懒加载、无限滚动、图表加载等需求。
Channel 是一个并发安全的阻塞队列,可以通过 send 函数往队列中塞入数据,通过 receive 函数从队列中取出数据。当队列被塞满时,send 函数将被挂起,直到队列有空闲缓存;当队列空闲时,receive 函数将被挂起,直到队列中有新数据存入。Channel 中队列缓存空间的大小需要在创建时指定,如果不指定,缓存空间默认是 0。
ORACLE LINUX 7.9 上安装 Caché数据库。该文介绍了在LINUX上安装Caché数据库的步骤,包括下载安装介质和访问链接信息。
文章浏览阅读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的关键是安装部署ssl证书,本文阐述了从证书申请到启用https的详细过程
在数据科学和机器学习领域,Kotlin的强大类型推断能力和函数式编程特性,使得数据处理和算法实现更加简洁和可读。此外,Kotlin还可用于游戏开发,特别是移动游戏开发,以及嵌入式系统的开发。它几乎可以运行在任何Java语言可以运行的地方,但相比Java,Kotlin更加简洁、高效和安全。它不仅可以编译成Java字节码,在Java虚拟机上运行,还可以编译成JavaScript,以便在没有JVM的设备上运行。此外,Kotlin还可以编译成二进制代码,直接运行在机器上,如嵌入式设备或iOS。
文章浏览阅读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 怎么选时间
详细介绍了引用循环出现的场景及为什么会出现引用循环,以及如何解决引用循环,引入了weak的概念。
指的是在项目无需重启的情况下,只需要刷新页面,即可获得已经修改的样式或功能。要注意该工具一般用于开发环境,在生产环境中最好不要添加这个工具。对于无需重启便可刷新这么方便的工具,在项目中该如何使用:在spring boot 项目中使用工具的方法就是引入相关依赖,热部署工具的依赖如下:
文章浏览阅读589次。SpringBean的注入原理spring是在配置类需要指定扫描包,然后递归得到下面所有的文件;(springboot默认启动类和兄弟目录下面所有的包文件)包名+文件名=类全限定名;calss.from加载到内存当中,得到字节码(class);判断这个类的脑门上是否有注解(就是类的头顶上),有注解的话,就把这个类先put到Map里面(ResourcesMap和autowiredMap各一..._springioc注入原理
文章浏览阅读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次。教育行业中大数据分析的主要目的包括改善学生成绩、服务教务设计、优化学生服务等。而学生成绩中有一系列重要的信息往往被我们常规研究所忽视。通过大数据分析和可视化展示,挖掘重要信息,改善 学生服务,对于教学改进意义重大。美国教育部门构建“学习分析系统”,旨在向教育工作者提供了解学生到底是在怎样学习的更好、更好、更精确信息。利用大数据的分析学习能够向教育工作者提供有用的信息,从而帮助其回答众多不易回答的现实问题。_对高中教学系统进行可视化分析,