-
图示:
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 System.Net;
//IPAdress,IPEndPoint(IP和端口)类
using System.Net.Sockets;
using System.Threading;
using System.IO;
namespace SocketChat
{
public partial
class Server : Form
{
public Server()
{
InitializeComponent();
TextBox.CheckForIllegalCrossThreadCalls
=
false;
//关闭对文本框的跨线程操作检查
}
Thread threadWatch
=
null;
//负责监听客户端服务请求的线程
Socket socketWatch
=
null;
//负责监听的套接字
private
void btnBeginListen_Click(
object sender, EventArgs e)
{
//创建 服务端 负责监听 的套接字 参数(使用IP4寻址协议,使用流式连接,使用TCP协议传输数据)
socketWatch
=
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取文本框中的IP地址
IPAddress address
= IPAddress.Parse(txtIP.Text.Trim());
//创建包含IP和端口的网络节点对象
IPEndPoint endpoint
=
new IPEndPoint(address,
int.Parse(txtPort.Text.Trim()));
//将负责监听的套接字绑定到唯一的IP和端口上
socketWatch.Bind(endpoint);
//设置监听队列的长度
socketWatch.Listen(
10);
//创建负责监听的线程,并传入监听方法
threadWatch
=
new Thread(WatchConnecting);
threadWatch.IsBackground
=
true;
//将线程设置为后台运行
threadWatch.Start();
//启动线程
ShowMsg(
"
服务器已启动监听!
");
}
//保存服务器端所有负责和客户端通信的套接字
Dictionary
<
string, Socket
> dict
=
new Dictionary
<
string, Socket
>();
//保存服务器端所有负责调用通信套接字.Receive()方法的线程
Dictionary
<
string, Thread
> dictThread
=
new Dictionary
<
string, Thread
>();
/// <summary>
/// 监听客户端请求
/// </summary>
void WatchConnecting()
{
while (
true)
//持续不断的监听客户端请求
{
//开始监听客户端请求 注意:Accept方法会阻断当前线程。
Socket sokConnection
= socketWatch.Accept();
//一旦监听到客户端的请求,就返回一个和该客户端通信的套接字cokConnection
//向列表控件中添加一个客户端的IP和端口字符串,作为客户端的唯一标识
lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
//将与客户端通信的套接字对象sokConnection添加到键值对集合中,并以客户端IP和端口作为键
dict.Add(sokConnection.RemoteEndPoint.ToString(),sokConnection);
//sokConnection.RemoteEndPoint保存的是当前连接客户端的IP和端口
//创建通信线程
Thread thr
=
new Thread(RecMsg);
thr.IsBackground
=
true;
//设置为后台运行
thr.Start(sokConnection);
dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr);
ShowMsg(
"
客户端连接成功!
"
+sokConnection.RemoteEndPoint.ToString());
}
}
/// <summary>
/// 服务端监听客户端发送的数据
/// </summary>
void RecMsg(
object socketClientPara)
{
Socket socketClient
= socketClientPara
as Socket;
while (
true)
{
//定义一个接收服务端数据的缓存区(2M字节数组)
byte[] arrMsgRec
=
new
byte[
1024
*
1024
*
2];
//将接收到的数据存入arrMsgRec数组,并返回真正接收到的数据的长度
int length
=
-1;
try
{
length
= socketClient.Receive(arrMsgRec);
}
catch (SocketException ex)
{
ShowMsg(
"
异常:
"
+ ex.Message);
//从通信套接字集合中删除被中断连接的通信套接字对象
dict.Remove(socketClient.RemoteEndPoint.ToString());
//从通信线程集合中删除被中断链接的通信线程对象
dictThread.Remove(socketClient.RemoteEndPoint.ToString());
//从列表中删除被中断连接的IP:PORT
lbOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());
break;
}
catch (Exception ex)
{
ShowMsg(
"
异常:
"
+ ex.Message);
break;
}
if (arrMsgRec[
0]
==
0)
//判断客户端发送过来的数据的第一个元素是0,则代表发来的是文字数据
{
//将接收到的服务端发来的消息转换成字符串(将所有元素【包含字符之外】转换)
string strMsgRec
= System.Text.Encoding.UTF8.GetString(arrMsgRec,
1, length
-
1);
//取有效数据
//显示接收到的消息
ShowMsg(strMsgRec);
}
else
if (arrMsgRec[
0]
==
1)
//如果是1,则代表发过来的是文件数据(图片、视频、文件......)
{
SaveFileDialog sfd
=
new SaveFileDialog();
//保存文件选择框
if (sfd.ShowDialog()
== System.Windows.Forms.DialogResult.OK)
{
string fileSavePath
= sfd.FileName;
//获取要保存的文件路径
//创建文件流,然后让文件流来根据路径创建一个文件
using (FileStream fs
=
new FileStream(fileSavePath, FileMode.Create))
{
fs.Write(arrMsgRec,
1, length
-
1);
ShowMsg(
"
文件保存成功,路径为:
"
+ fileSavePath);
}
}
}
}
}
/// <summary>
/// 显示连接信息
/// </summary>
/// <param name="msg"></param>
void ShowMsg(
string msg)
{
txtMsg.AppendText(msg
+
"
\r\n
");
}
//发送消息到客户端
private
void btnSend_Click(
object sender, EventArgs e)
{
if (
string.IsNullOrEmpty(lbOnline.Text))
{
MessageBox.Show(
"
请选择要发送的客户端!
");
}
else
{
//获取输入内容
string strMsg
= txtMsgSend.Text.Trim();
//将要发送的字符串转成UTF8对应的字节数组
byte[] arrMsg
= System.Text.Encoding.UTF8.GetBytes(strMsg);
//获取列表中被选中的远程IP
string strClientKey
= lbOnline.Text;
try
{
//通过KEY,找到字典集合中对应的与某个客户端通信的套接字,并用send方法,发送数据给对方。
dict[strClientKey].Send(arrMsg);
ShowMsg(
"
向客户端发送了数据:
"
+ strMsg);
}
catch (SocketException ex)
{
ShowMsg(
"
发送异常:
"
+ ex.Message);
}
catch (Exception ex)
{
ShowMsg(
"
发送异常:
"
+ ex.Message);
}
}
}
/// <summary>
/// 服务器群发消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private
void btnSendToAll_Click(
object sender, EventArgs e)
{
//获取输入内容
string strMsg
= txtMsgSend.Text.Trim();
//将要发送的字符串转成UTF8对应的字节数组
byte[] arrMsg
= System.Text.Encoding.UTF8.GetBytes(strMsg);
foreach (Socket s
in dict.Values)
{
s.Send(arrMsg);
}
ShowMsg(
"
消息群发完毕!
");
}
}
}