using System; //包含基本类和基类
using System.Collections.Generic; //用于定义泛型集合的接口和类
using System.ComponentModel; //实现组件设计和运行时行为的类
using System.Data; //提供了对数据库的访问支持
using System.Drawing; //提供了对图形绘制的支持
using System.Text; //包含用于字符编码等功能的对象
using System.Windows.Forms; //支持Windows窗体的类
using System.Net; //为当前网络上使用的多种协议提供了简单的编程接口
using System.Net.Sockets; //为网络编程开发人员提供了Windows Sockets (Winsock)接口的托管实现
using System.Threading; //提供了一些可以进行多线程编程的类和接口
using System.Collections; //包含可用的集合类和相关的接口,提供了集合的基本功能
namespace AgentServer //程序的命名空间,也是项目名称AgentServer
{
public partial class ServerForm : Form //窗口类
{
struct ClientInfo //结构体ClientInfo struct语句为程序定义了一个带有多个成员的新的数据类型,struct不能指定为protected
{
public EndPoint endpoint; //公有的终结点 endpoint //网络地址和端口号的组合称为终结点;EndPoint类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息
public string strName; //公有的字符串变量 strName
}
ArrayList clientList; //ArrayList在System.Collections中,定义了ArrayList类型数组clientList
string ServerIp; //字符串变量 ServerIP
string ServerPort; //字符串变量ServerPort
Socket serverSocket; //定义Socket变量serverSocket
byte[] byteData = new byte[1024]; //定义一个byte类型一维数组byteData,它有1024个元素 byte无符号整数 0-255
public ServerForm() //窗体类ServerForm的构造函数
{
CheckForIllegalCrossThreadCalls = false; //在多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,如果需要访问窗口中的控件,可以在窗口构造函数中将CheckForIllegalCrossThreadCalls设置为false
clientList = new ArrayList(); //新建数组clientList
InitializeComponent(); //完成初始化操作
ServerIp = System.Configuration.ConfigurationSettings.AppSettings["ServerIp"]; //读取配置文件信息,ConfigurationSettings类在命名空间System.Configuration中,AppSettings是ConfigurationSettings的属性,ServerIp = 127.0.0.1
ServerPort = System.Configuration.ConfigurationSettings.AppSettings["ServerPort"]; //读取配置文件ServerPort = 1110 #Client status info
try //抛出异常
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //创建一个Socket实例,基于UDP的网络协议 //Socket类的构造函数使用三个参数来定义创建的Socket实例,AddressFamily用来指定网络类型,SocketType用来指定套接字类型,ProtocolType用来指定网络协议,三个参数都是在命名空间System.Net.Sockets中定义的枚举类型,它们不能任意组合;若是基于TCP应该serverSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(ServerIp), int.Parse(ServerPort)); //用ServerIp和ServerPort初始化IPEndPoint类的实例ipEndPoint ;IPAddress.Parse(ServerIP)将ServerIp字符串转换为IPAddress实例;int.Parse(ServerPort)将数字类型字符串ServerPort转换为int型 //IPEndPoint类包含应用程序连接到主机上的服务所需的主机和端口号信息,通过组合服务的主机IP地址和端口号,IPEndPoint类形成到服务的连接点
serverSocket.Bind(ipEndPoint); //服务器端套接字serverSocket与终结点ipEndPoint相关联
IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0); //初始化IPEndPoint类的实例 ipeSender;IPAddress.Any表示本地系统可用的任何IP地址,指示服务器应侦听所有网络接口上的客户端活动;端口号0是为了避免写死端口号的做法,也是为了从本地系统中找到可用端口,故用0表示连接参数
EndPoint epSender = (EndPoint)ipeSender; //强制类型转换,将IPEndPoint类型的ipeSender转换为EndPoint类型的epSender
// byteData = new byte[1024]; //Socket.BeginReveiveFrom(buffer, offset, size, socketFlags,remoteEP,callback,state ) buffer接收数据的存储位置,offset从零开始的位置,buffer在其中存储数据的参数,size要接收的字节数,socketFlags是SocketFlags值的按位组合,remoteEP是EndPoint表示的数据源,callback是AsyncCallback委托,state包含此请求状态信息的对象
serverSocket.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref epSender, new AsyncCallback(OnReceive), epSender); //以异步的方式从指定的网络设备上接收数据;数组byteData是接收到的数据的存储位置,0表示接收数据存储在byteData从0开始的位置,byteData.Length表示接收数据的大小,SocketFlags.None表示对这次调用不使用标志,SocketFlags枚举类型参数指定套接字的发送和接收行为,ref epSender终结点,ref表示引用传递,AsyncCallback委托,在异步调用完成时要调用的方法,
if (lbUsers.Items.Count <= 0) //lbUser.Items.Count表示列表框lbUser中列表项的个数 //lbUsers是ListBox控件,ListBox控件的作用是显示长的文件列表
{
btnSendMessage.Enabled = false; //Button btnSendMessage的Enabled属性设置为false,也就是使控件不可用,禁止该控件与用户或程序交互
btnBrocast.Enabled = false; //Button btnBrocast的Enabled属性设置为false
}
}
catch (Exception ex) //捕获异常
{
MessageBox.Show(ex.Message, "ServerForm:UdpTcpServer", MessageBoxButtons.OK, MessageBoxIcon.Error); //使用消息对话框给用户提示,对话框中有OK按钮和Error按钮
}
}
private void btnSendMessage_Click(object sender, EventArgs e) //btnSendMessage按钮的单击事件
{
if (lbUsers.SelectedItems.Count > 0) //lbUsers.Selected.Count是用户选择的项目数
{
if (txtMessage.Text.Length > 0) //文本框textMessage中字符个数大于0
{
string sendMessage = txtMessage.Text; //发送的消息是“talk|Server|txtMessage内容|”
byte[] byteDataSend = new byte[1024]; //定义一个byte类型数组byteDataSend,它有1024个元素
byteDataSend = Encoding.ASCII.GetBytes(sendMessage); //将sendMessage这个字符串根据ASCII编码采用byte表示并赋值给byteDataSend;
foreach (string user in lbUsers.SelectedItems) //在lbUsers列表框选中user
{
foreach (ClientInfo client in clientList) //迭代clientList中的client
{
if (client.strName.ToString().Trim() == user.Trim()) //将client的strName属性转换为字符串类型,并去掉前后空格
{
serverSocket.BeginSendTo(byteDataSend, 0, byteDataSend.Length, SocketFlags.None, client.endpoint, new AsyncCallback(OnSend), client.endpoint); //以异步的方式将数据发送到特定的远程主机 Sockets.BeginSendTo方法(Byte[], Int32, Int32, SocketFlags, EndPoint, AsyncCallback,Object)
} //byteDataSend要发送的数据,0表示byteDataSend从位置0开始发送数据,byteDataSend.Length表示要发送的字节数,SocketFlags.None表示这次调用不使用标志,client.endpoint表示客户端,也就是包含此请求信息的对象
}
}
txt_output.AppendText("服务器:" txtMessage.Text Environment.NewLine); //RichTextBox除了具有TextBox的所有功能外,还能提供富文本显示的功能 //在富文本框rtbHistoryMessage的文本末尾追加“服务器:txtMessage的内容换行”Environment.NewLine属性是获取为此环境定义的换行符
txtMessage.Text = null; //文本框txtMessage属性置为空
}
else
{
MessageBox.Show("不能发送空信息!"); //向用户显示消息“不能发送空信息!”
}
}
else
{
MessageBox.Show("请选择用户,再发送!"); //向用户显示“请选择用户,再发送!”
}
}
public void OnSend(IAsyncResult ar) //IAsyncResult接口表示异步操作的状态 ar存储异步操作的状态信息
{
try //抛出异常
{
serverSocket.EndSend(ar); //serverSocket.EndSend(ar)结束挂起的异步发送
}
catch (Exception ex) //捕获异常
{
MessageBox.Show(ex.Message, "OnSend:UdpTcpServer", MessageBoxButtons.OK, MessageBoxIcon.Error); //使用消息对话框给用户提示,对话框中有OK按钮和Error按钮
}
}
private void OnReceive(IAsyncResult ar) //IAsyncResult接口表示异步操作的状态 ar存储异步操作的状态信息
{
try //抛出异常
{
IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0); //初始化IPEndPoint类的实例 ipeSender;IPAddress.Any表示本地系统可用的任何IP地址,指示服务器应侦听所有网络接口上的客户端活动;端口号0是为了避免写死端口号的做法,也是为了从本地系统中找到可用端口,故用0表示连接参数
EndPoint epSender = (EndPoint)ipeSender; //强制类型转换,将IPEndPoint类型的ipeSender转换为EndPoint类型的epSender
serverSocket.EndReceiveFrom(ar, ref epSender); //结束挂起的异步读取从特定的终结点 serverSocket.EndReceiveFrom(IAsyncResult, EndPoint) IAsyncResult用于存储状态信息和对此异步操作的任何用户定义数据,EndPoint源EndPoint
string msgReceived = Encoding.ASCII.GetString(byteData).TrimEnd('\0').Trim(); //将byteData这个字符串根据ASCII编码采用string表示,并赋值给msgReceived
string[] tokens = msgReceived.Split(new char[] { '|' }); //将字符串msgReceived使用"|"进行拆分,并将拆分后结果赋给字符串类型数组tokens
if (tokens.Length > 4)
return;
if (tokens[0] == "login") //若数组tokens的第零个元素是"login" Begin|Login|ClientName|End
{
ClientInfo clientInfo = new ClientInfo(); //声明clientInfo,类型为ClientInfo
clientInfo.endpoint = epSender; //clientInfo详述,endpoint属性为epSender
clientInfo.strName = tokens[1].TrimEnd('\0'); //strName为tokens第一个元素
int nIndex = 0; //索引值nIndex初始化为0
foreach (ClientInfo client in clientList) //迭代clientList中的client
{
string client_name = client.strName.ToString().Trim(); //client的strName属性转换为字符串并去掉前后空格,然后赋值给client_name
string strEndPoint = client.endpoint.ToString(); //client的endpoint属性转换为字符串然后赋值给strEndPoint
string strepSender = epSender.ToString(); //epSpender转换为字符串然后赋值给strepSender
if (client_name == tokens[1]) //client_name为tokens数组第一个元素
{
clientList.RemoveAt(nIndex); //移除clientList索引nIndex处的元素
lbUsers.Items.RemoveAt(nIndex); //移除将列表框lbUsers索引项nIdex
break;
}
nIndex;
}
clientList.Add(clientInfo); //将clientInfo添加到clientList末尾
lbUsers.Items.Add(tokens[1]); //将tokens[1]添加到列表框lbUsers中
btnBrocast.Enabled = true; //使按钮btnBroadcast可用,可与用户或程序交互
btnSendMessage.Enabled = true; //使按钮btnSendMessage可用,可与用户或程序交互
string sendmessage = "online"; //定义字符串变量sendmessage为"online"
byteData = Encoding.ASCII.GetBytes(sendmessage); //将字符串变量sendmessage根据ASCII编码表示成byte类型并赋值给byteData
serverSocket.BeginSendTo(byteData, 0, byteData.Length, SocketFlags.None, clientInfo.endpoint, new AsyncCallback(OnSend), clientInfo.endpoint); //以异步的方式将数据发送到特定的远程主机
//byteData要发送的数据,0表示byteData从位置0开始发送数据,byteData.Length表示要发送的字节数,SocketFlags.None表示这次调用不使用标志,clientInfo.endpoint表示客户端,也就是包含此请求信息的对象
string output = tokens[1].TrimEnd('\0') "已登录!" "\r\n";
int tokenlength=tokens[1].Trim().Length;
txt_output.AppendText(output);
}
else if (tokens[0] == "logout") //若数组tokens的第零个元素是"logout"
{
int nIndex = 0; //索引值nIndex初始化为0
foreach (ClientInfo client in clientList) //迭代clientList中的client
{
string strEndPoint = client.endpoint.ToString(); //client的endpoint属性转换为字符串并赋值给strEndPoint
string strepSender = epSender.ToString(); //epSender转换为字符串并赋值给strepSender
if (strEndPoint.Equals(strepSender)) //strEndPoint与strepSender引用相等
{
clientList.RemoveAt(nIndex); //移除clientList中索引nIndex元素
lbUsers.Items.RemoveAt(nIndex); //移除列表框lbUser索引项nIndex
break;
}
nIndex;
}
string strMessage = tokens[1]; //将数组tokens第一个元素赋值给strMessage
string output = tokens[1].TrimEnd('\0') "已退出!" "\r\n";
txt_output.AppendText(output); //将strMessage添加到富文本框rtbHistoryMessage中
}
else if (tokens[0] == "talk" && tokens.Length==4) //数组tokens第零个元素为"talk"
{
string source = tokens[1];
string distinction = tokens[2];
string message = tokens[3];
txt_output.AppendText(msgReceived "\r\n");
string sendMessage = msgReceived; //发送的消息是“talk|Server|txtMessage内容|”
byte[] byteDataSend = new byte[1024]; //定义一个byte类型数组byteDataSend,它有1024个元素
byteDataSend = Encoding.ASCII.GetBytes(sendMessage.TrimEnd('\0')); //将sendMessage这个字符串根据ASCII编码采用byte表示并赋值给byteDataSend;
foreach (ClientInfo client in clientList) //迭代clientList中的client
{
if (client.strName.ToString().TrimEnd('\0').Trim() == distinction) //将client的strName属性转换为字符串类型,并去掉前后空格
{
serverSocket.BeginSendTo(byteDataSend, 0, byteDataSend.Length, SocketFlags.None, client.endpoint, new AsyncCallback(OnSend), client.endpoint); //以异步的方式将数据发送到特定的远程主机 Sockets.BeginSendTo方法(Byte[], Int32, Int32, SocketFlags, EndPoint, AsyncCallback,Object)
}
}
}
byteData = new byte[1024]; //定义一个byte类型数组byteData,它有1024个元素
serverSocket.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref epSender, new AsyncCallback(OnReceive), epSender);
} //以异步的方式从指定的网络设备上接收数据;数组byteData是接收到的数据的存储位置,0表示接收数据存储在byteData从0开始的位置,byteData.Length表示接收数据的大小,SocketFlags.None表示对这次调用不使用标志,SocketFlags枚举类型参数指定套接字的发送和接收行为,ref epSender终结点,ref表示引用传递,AsyncCallback委托,在异步调用完成时要调用的方法,
catch (Exception ex) //抛出异常
{
MessageBox.Show(ex.Message, "OnReceive:UdpTcpServer", MessageBoxButtons.OK, MessageBoxIcon.Error); //使用消息对话框给用户提示,对话框中有OK按钮和Error按钮
}
}
private void ServerSendtoClient(string message, EndPoint clientednpoint) //server端向clieent端发送消息
{
string sendmessage = "talk|Server|" message "\n"; //字符串变量sendmessage赋值为“talk|Server|message内容|换行”
byteData = Encoding.ASCII.GetBytes(sendmessage); //将sendmessage根据ASCII编码转换为byte类型并赋值给byteData
serverSocket.BeginSendTo(byteData, 0, byteData.Length, SocketFlags.None, clientednpoint, new AsyncCallback(OnSend), clientednpoint); //以异步方式将数据发送到远程主机
} //byteData要发送的数据,0表示byteData从位置0开始发送数据,byteData.Length表示要发送的字节数,SocketFlags.None表示这次调不使用标志,clientedpoint表示客户端,也就是包含此请求信息的对象
/* private void OnProcessMessage(string clientname, string message, EndPoint epSender) //
{
string[] command = message.Split(new char[] { ':' }); //将字符串message使用":"进行拆分,并将拆分后结果赋给字符串类型数组command
foreach (ClientInfo client in clientList) //迭代clientList中的client
{
string client_name=client.strName.ToString().Trim(); //将client的strName属性转换为字符串,然后去掉前后空格并赋值给client_name
if (client_name == command[1].Trim()) //client_name为FireControlPanel火控控制面板
{
ServerSendtoClient(message, client.endpoint); //调用ServerSendtoClient函数
}
}
}*/
private void btnBrocast_Click(object sender, EventArgs e) //按钮btnBroad_Click的单击事件
{
string sendMessage = "talk|" "ServerBrcas|" txtMessage.Text "|\n"; //字符串变量sendMessage为 “talk|服务器txtMessage内容换行”
byte[] byteDataBrocast = new byte[1024]; //定义一个byte类型数组byteDataBroadcast,它有1024个元素
byteDataBrocast = Encoding.ASCII.GetBytes(sendMessage); //将sendMessage根据ASCII编码转换为byte
foreach (ClientInfo ci in clientList) //迭代clientList
{
EndPoint epSender = ci.endpoint; //将ci的endpoint属性赋值给epSender
serverSocket.BeginSendTo(byteDataBrocast, 0, byteDataBrocast.Length, SocketFlags.None, epSender, new AsyncCallback(OnSend), epSender);
} //以异步方式将数据发送到远程主机
txt_output.AppendText("服务器广播:" txtMessage.Text Environment.NewLine); //富文本框显示“服务器:txtMessage内容换行”
txtMessage.Text = null; //txtMessage置为空
sendMessage = null; //sendMessage置为空
}
private void lbUsers_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void rtbHistoryMessage_TextChanged(object sender, EventArgs e)
{
}
}
}
评论