实例介绍
【实例简介】
Socket服务端功能要求:
1.软件不能被误操作关闭,禁用右上角关闭按钮,可在任务栏右下角图标上点右键关闭,并且弹窗确认。
2.开机自启动,软件启动时就启动Socket服务。
3.界面上显示实时已连接的客户端的IP,端口号和工位名称。
4.可选择客户端单独发送关机、重启指令,也可群发关机指令,全部关机有密码输入弹窗。
5.在配置文件里配置绑定的本机IP和端口号,以及所有工位名称与IP的键值对。
6.软件界面有Socket服务已启动,正在监听中的实时状态指示灯。
7.服务端关闭,不影响客户端运行,客户端正常断开,服务端再打开,客户端正常连接。
8.网线断开(IP不对,防护墙/杀软拦截),不影响服务端运行,客户端端正常断开,网线再连上,客户端正常再连接。
Socket客户端功能要求:
1.软件不能被误操作关闭,禁用右上角关闭按钮,可在任务栏右下角图标上点右键关闭,并且弹窗确认。
2.开机自启动,软件启动时就连接Socket服务端。
3.界面上显示所连接服务端的IP,端口号。客户端启动时默认最小化界面,不耽误操作其他软件。
4.在配置文件里配置要连接的目标服务端IP和端口号,
5.软件界面有已连接Socket服务端的实时状态指示灯,断开连接后每隔2秒自动重连,直到连接上为止,
连接了就不能再重连。
6.客户端关机前弹个窗,提示即将关机。
7.客户端关闭,不影响服务端运行,客户端正常断开,客户端再打开,客户端正常连接。
8.网线断开(IP不对,防护墙/杀软拦截),不影响客户端运行,客户端正常断开,网线再连上,客户端正常再连接。
【实例截图】
【核心代码】
using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace SocketTestServer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
TextBox.CheckForIllegalCrossThreadCalls = false;//消除对textbox控件的线程检测
txtIP.Text = ReadConfigFile("ipAddress");
txtPort.Text = ReadConfigFile("port");
//软件启动时创建服务
try
{
IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));//第一步:用指定的端口号和服务器的ip建立一个EndPoint对像
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//第二步:建立一个Socket对像
socketWatch.Bind(endpoint);//第三步:用socket对像的Bind()方法绑定EndPoint
socketWatch.Listen(100);//第四步:用socket对像的Listen()方法开始监听(挂起连接池的最大长度,提前准备100个挂起的连接,提高高并发性能)
threadWatch = new Thread(WatchConnection);//创建Socket服务器线程
threadWatch.IsBackground = true;//设置为后台线程,窗体关闭线程可随之关闭
threadWatch.Start();
ShowMsg("Socket服务器启动监听成功......");
label3.Text = "服务已启动,正在监听中";
label3.BackColor = Color.Green;
label3.ForeColor = Color.White;
}
catch (Exception ex)
{
ShowMsg2(ex.ToString());
}
}
//获取Configuration对象
Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
/// <summary>
/// 根据Key读取配置文件中元素的Value
/// </summary>
/// <param name="keyName"></param>
/// <returns></returns>
public string ReadConfigFile(string keyName)
{
try
{
//根据Key读取元素的Value
string name = config.AppSettings.Settings[keyName].Value;
//Form1.form1.ReadLog("配置参数" keyName "读取成功");
ShowMsg("配置参数" keyName "读取成功");
return name;
}
catch (Exception ex)
{
ShowMsg2("配置参数" keyName "读取失败");
return null;
}
}
Thread threadWatch = null;
Socket socketWatch = null;
/// <summary>
/// 创建服务
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
try
{
IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));//第一步:用指定的端口号和服务器的ip建立一个EndPoint对像
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//第二步:建立一个Socket对像
socketWatch.Bind(endpoint);//第三步:用socket对像的Bind()方法绑定EndPoint
socketWatch.Listen(100);//第四步:用socket对像的Listen()方法开始监听(挂起连接池的最大长度,提前准备100个挂起的连接,提高高并发性能)
threadWatch = new Thread(WatchConnection);//创建Socket服务器线程
threadWatch.IsBackground = true;//设置为后台线程,窗体关闭线程可随之关闭
threadWatch.Start();
ShowMsg("Socket服务器启动监听成功......");
label3.Text = "服务已启动,正在监听中";
label3.BackColor = Color.Green;
label3.ForeColor = Color.White;
}
catch (Exception ex)
{
ShowMsg2(ex.ToString());
}
}
//定义2个字典
Dictionary<string, Socket> dictSocket = new Dictionary<string, Socket>();
Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();
/// <summary>
/// 监听客户端请求连接的方法(线程方法),每监听到一个连接,就创建一个线程为该连接进行处理,实现Socket监听多个连接
/// </summary>
/// <param name="obj"></param>
private void WatchConnection(object obj)
{
while (true)
{
// 负责通信的套接字
Socket sokConnection = socketWatch.Accept();//第五步:监听客户端的连接,用socket对像的Accept()方法创建新的socket对像和请求的客户端通信(没有新连接时线程就等在这里)
string online_ipandport = sokConnection.RemoteEndPoint.ToString();
string online2_ip = online_ipandport.Substring(0, online_ipandport.LastIndexOf(":"));
string online3_workposition= ReadConfigFile(online2_ip);
lb_Online.Items.Add(online_ipandport "|" online3_workposition);//加入在线列表控件显示
dictSocket.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);//加入通信套接字集合
//创建通信线程
Thread thr = new Thread(RecMsg);//每监听到一个连接,就创建一个线程为该连接处理收发数据,实现Socket监听多个连接
thr.IsBackground = true;
thr.Start(sokConnection);//注意传入RecMsg方法Accept()来的套接字对象
dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr);//加入通信线程集合
ShowMsg("客户端连接成功......" sokConnection.RemoteEndPoint.ToString());
}
}
/// <summary>
/// 监听客户端发送消息的方法(线程方法)
/// </summary>
void RecMsg(object socketClient)
{
Socket socketClients = socketClient as Socket;
while (true)
{
byte[] arrMsgRec = new byte[1024 * 1024 * 2];//大小为2Mb的字节数组
int length = -1;
try
{
length = socketClients.Receive(arrMsgRec);//第六步,用socket对像的Receive()接收数据放到字节数组中,没有新数据时线程就等在这里
}
catch (SocketException ex)
{
ShowMsg2("异常:" ex.Message socketClients.RemoteEndPoint.ToString());
// 从通信套接字集合中删除被中断连接的套接字对象
dictSocket.Remove(socketClients.RemoteEndPoint.ToString());
// 从通信线程集合中删除被中断连接的套接字对象
dictThread.Remove(socketClients.RemoteEndPoint.ToString());
// 从列表中移除 IP&Port
string online_ipandport = socketClients.RemoteEndPoint.ToString();
string online2_ip = online_ipandport.Substring(0, online_ipandport.LastIndexOf(":"));
string online3_workposition = ReadConfigFile(online2_ip);
lb_Online.Items.Remove(online_ipandport "|" online3_workposition);//从在线列表控件移除
break;//跳出循环,结束循环,线程就不执行事务了,如果少了break,在客户端关闭时,Socket.Receive方法会接收到异常数据,造成软件崩溃
}
catch (Exception ex)
{
ShowMsg2("异常:" ex.Message);
break;//跳出循环,结束循环,线程就不执行事务了
}
// 判断第一个发送过来的数据(字节)如果是1,则代表发送过来的是文本数据
if (arrMsgRec[0] == 0)
{
string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1);
ShowMsg(strMsgRec);
}
// 若是1则发送过来的是文件数据(文档,图片,视频等。。。)
else if (arrMsgRec[0] == 1)
{
// 保存文件选择框对象
SaveFileDialog sfd = new SaveFileDialog();
// 选择文件路径
if (sfd.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)//sfd.ShowDialog()括号中必须加入this,否则不会弹窗
{
// 获得保存文件的路径
string fileSavePath = sfd.FileName;
// 创建文件流,然后让文件流根据路径创建一个文件
using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
{
fs.Write(arrMsgRec, 1, length - 1);
ShowMsg("文件保存成功:" fileSavePath);
}
}
}
}
}
//正常消息
private void ShowMsg(string p)
{
string Time = Convert.ToString(DateTime.Now);
txtMsg.SelectionColor = Color.Black;
txtMsg.AppendText(Time " " p "\n");
txtMsg.ScrollToCaret();//光标滚到最后一行
}
//异常消息
private void ShowMsg2(string p)
{
string Time = Convert.ToString(DateTime.Now);
txtMsg.SelectionColor = Color.Red;
txtMsg.AppendText(Time " " p "\n");
txtMsg.ScrollToCaret();//光标滚到最后一行
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(lb_Online.Text))//ListBox当前选中的文本
{
MessageBox.Show("请选择发送的好友!!!");
}
else
{
string strMsg = tb_Msg_Send.Text.Trim();
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
string strClientKey = lb_Online.Text.Substring(0, lb_Online.Text.IndexOf("|"));
try
{
dictSocket[strClientKey].Send(arrMsg);
ShowMsg("I say:" strMsg);
}
catch (SocketException ex)
{
ShowMsg2("发送时异常:" ex.Message);
}
catch (Exception ex)
{
ShowMsg2("发送时异常:" ex.Message);
}
}
}
/// <summary>
/// 群发消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
string strMsg = tb_Msg_Send.Text.Trim();
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
foreach (Socket item in dictSocket.Values)
{
item.Send(arrMsg);
}
ShowMsg("群发完毕!!!:)");
}
/// <summary>
/// 右键关闭软件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem1_Click(object sender, EventArgs e)
{
//foreach (KeyValuePair<string, Socket> kvp in dictSocket)
//{
// try
// {
// kvp.Value.Shutdown(SocketShutdown.Both);
// }
// catch (SocketException ex)
// {
// ShowMsg2("发送时异常:" ex.Message);
// }
// catch (Exception ex)
// {
// ShowMsg2("发送时异常:" ex.Message);
// }
// kvp.Value.Close();
//}
Application.Exit();
}
/// <summary>
/// 最小化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button6_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
/// <summary>
/// 控制richTeztBox1的显示行数,并删除超出行数并保持剩余行颜色不变,同时始终保证滚动条和光标在最后一行
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtMsg_TextChanged(object sender, EventArgs e)
{
if (txtMsg.Lines.Length > 100)
{
txtMsg.SelectionStart = 0;
txtMsg.SelectionLength = txtMsg.GetFirstCharIndexFromLine(10);//一次选10行删掉,因为最大一次消息也不会超过10行,如果填1就只能删一行,会导致行数越来越多
txtMsg.SelectedText = "..." "\n";//如果赋空字符串就不能正常删除超出的行,也不能保留颜色,必须给点什么
//光标聚焦在最后一行
txtMsg.Select(txtMsg.Text.Length, 0);
txtMsg.ScrollToCaret();
}
}
/// <summary>
/// 单独关机
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(lb_Online.Text))//ListBox当前选中的文本
{
MessageBox.Show("请选择发送的好友!!!");
}
else
{
string strMsg = "关机";
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
string strClientKey = lb_Online.Text.Substring(0, lb_Online.Text.IndexOf("|"));
try
{
dictSocket[strClientKey].Send(arrMsg);
ShowMsg("向客户端" lb_Online.Text "发送消息:" strMsg);
}
catch (SocketException ex)
{
ShowMsg2("发送时异常:" ex.Message);
}
catch (Exception ex)
{
ShowMsg2("发送时异常:" ex.Message);
}
}
}
/// <summary>
/// 全部关机
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button5_Click(object sender, EventArgs e)
{
string strMsg = "关机";
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
if (dictSocket.Count != 0)
{
string str = Interaction.InputBox("请输入密码", "密码", "", 680, 400);//进入设置页面前必须输入密码,使用VB里面的InputBox控件,必须引用VB.dll
if (str == "87671678") //如果输入密码正确,才执行单击按钮的事件响应内容,否则返回,什么都不做
{
foreach (KeyValuePair<string, Socket> kvp in dictSocket)
{
try
{
kvp.Value.Send(arrMsg);
}
catch (SocketException ex)
{
ShowMsg2("发送时异常:" ex.Message);
}
catch (Exception ex)
{
ShowMsg2("发送时异常:" ex.Message);
}
ShowMsg("向客户端" kvp.Key "发送消息:" strMsg);
}
ShowMsg("群发完毕!!!:)");
}
else
{
return;
}
}
else
{
MessageBox.Show("没有连接对象!!!");
}
}
/// <summary>
/// 单独重启
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button7_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(lb_Online.Text))//ListBox当前选中的文本
{
MessageBox.Show("请选择发送的好友!!!");
}
else
{
string strMsg = "重启";
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
string strClientKey = lb_Online.Text.Substring(0, lb_Online.Text.IndexOf("|"));
try
{
dictSocket[strClientKey].Send(arrMsg);
ShowMsg("向客户端" lb_Online.Text "发送消息:" strMsg);
}
catch (SocketException ex)
{
ShowMsg2("发送时异常:" ex.Message);
}
catch (Exception ex)
{
ShowMsg2("发送时异常:" ex.Message);
}
}
}
/// <summary>
///右键显示
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolStripMenuItem2_Click(object sender, EventArgs e)
{
this.Visible = true;
this.WindowState = FormWindowState.Normal;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//Form1关闭时,弹窗提示,防止误关
DialogResult TS = MessageBox.Show("确定退出?退出则客户端将全部失去连接", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (TS == DialogResult.Yes)
e.Cancel = false;
else
e.Cancel = true;
}
}
}
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
网友评论
我要评论