Socket通信与多线程
服务器端
public Form1()
{
InitializeComponent();
//在程序加载的时候取消跨线程的检测
Control.CheckForIllegalCrossThreadCalls = false;
}
/// <summary>
/// 开始监听
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
try
{
//IP地址(InterNetwork表示IPV4)/使用TCP就是使用数据流/使用的流式的Socket,对应的为Tcp协议
Socket sockWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建IP对象
IPAddress ip = IPAddress.Parse(this.txtServer.Text);
//直接获取本机的IP地址
//IPAddress ip = IPAddress.Any;
//创建端口号对象
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.txtPort.Text));
//绑定IP/端口号信息
sockWatch.Bind(point);
ShowMessage("监听开始......");
//设置监听队列,限定最大连接数,到达最大连接数的时候需要排队
sockWatch.Listen(10);
//创建一个新的线程来持续的监听
Thread th1 = new Thread(Listen);
th1.IsBackground = true;
th1.Start(sockWatch);
}
catch
{
}
}
/// <summary>
/// 保存客户端连接时创建的用来通信的Socket的集合
/// </summary>
Dictionary<string, Socket> SocketSend = new Dictionary<string, Socket>();
/// <summary>
/// 负责监听
/// </summary>
/// <param name="obj"></param>
private void Listen(object obj)
{
Socket socketWatch = obj as Socket;
//循环监听
while (true)
{
try
{
//负责监听的Socket,用来接收客户端的连接,并且创建负责通信的Socket
//等待客户端的连接
//这里使用循环来监听,并且放到线程中,防止卡死主线程
Socket sockSend = socketWatch.Accept();
ShowMessage(sockSend.RemoteEndPoint.ToString() + "连接成功......");
//添加到下拉列表列表中
cboUsers.Items.Add(sockSend.RemoteEndPoint.ToString());
//添加到字典中
SocketSend.Add(sockSend.RemoteEndPoint.ToString(), sockSend);
//开启一个线程来不断的接收客户端发送的信息,传入的参数为当前新增的Socket线程
Thread th2 = new Thread(Rec);
th2.IsBackground = true;
th2.Start(sockSend);
}
catch
{
}
}
}
/// <summary>
/// 不断的接收客户端发送的信息
/// </summary>
/// <param name="obj"></param>
private void Rec(object obj)
{
Socket sockSend = obj as Socket;
while (true)
{
try
{
//创建一个字符数组用来接收客户端发送的信息
byte[] buffer = new byte[1024 * 1024 * 5];
//接收客户端发送的信息
int r = sockSend.Receive(buffer);
//如果接收到的信息为空,说明客户端下线了
if (r == 0)
{
break;
}
//解析接收到的数组信息
string str = System.Text.Encoding.UTF8.GetString(buffer, 0, r);
ShowMessage(sockSend.ReceiveTimeout.ToString() + ":" + str);
}
catch
{
}
}
}
/// <summary>
/// 界面信息
/// </summary>
/// <param name="str"></param>
public void ShowMessage(string str)
{
txtLog.AppendText(str + "\r\n");
}
/// <summary>
/// 发送信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
//把文本文件转换成字节数组
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(this.txtMsg.Text);
//创建协议,来确定传输的是什么数据
List<byte> list = new List<byte>();
//增加一个协议规则(这里需要处理,这样太简单)
list.Add(0);
list.AddRange(buffer);
//重新转换为一个字节数组
byte[] newBuffer = list.ToArray();
//发送的客户端对象不能为空
if (this.cboUsers.SelectedItem == null || this.cboUsers.SelectedItem.ToString() == "")
{
MessageBox.Show("请选择客户信息");
return;
}
//获取选中的客户端
string ip = this.cboUsers.SelectedItem.ToString();
//发送信息
SocketSend[ip].Send(newBuffer);
}
/// <summary>
/// 选择文件,并把文件路径显示在文本框中
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelect_Click(object sender, EventArgs e)
{
//创建一个打开文本框的对象
OpenFileDialog opf = new OpenFileDialog();
//设置文本框的提示信息
opf.Title = "请选择需要发送的文件";
//设置默认打开文件件
opf.InitialDirectory = @"C:\Users\Administrator\Desktop\test";
//设置选择文件类型
opf.Filter = "图片|*.jpg|视频|*.mav|所有文件|*.*";
opf.ShowDialog();
//把获取的路径放到文本框里
this.txtPath.Text = opf.FileName;
}
/// <summary>
/// 发送文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendFile_Click(object sender, EventArgs e)
{
//获取需要发送的文件路径
string path = this.txtPath.Text.ToString();
//创建一个数据流的对象
FileStream fsRead = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read);
//创建一个字节数组来接收读取的信息
byte[] buffer = new byte[fsRead.Length];
int r = fsRead.Read(buffer, 0, buffer.Length);
//释放流的资源
fsRead.Dispose();
fsRead.Close();
//创建协议对数据进行处理
List<byte> list = new List<byte>();
list.Add(1);
list.AddRange(buffer);
byte[] newBuffer = list.ToArray();
//发送的客户端对象不能为空
if (this.cboUsers.SelectedItem == null || this.cboUsers.SelectedItem.ToString() == "")
{
MessageBox.Show("请选择客户信息");
return;
}
//获取客户端IP地址
string ip = this.cboUsers.SelectedItem.ToString();
//发送信息
SocketSend[ip].Send(newBuffer);
}
/// <summary>
/// 震荡
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnZD_Click(object sender, EventArgs e)
{
//创建一个字节数组,只有一个元素,保存表示震动的协议
byte[] buffer = new byte[1];
buffer[0] = 2;
//发送的客户端对象不能为空
if (this.cboUsers.SelectedItem == null || this.cboUsers.SelectedItem.ToString() == "")
{
MessageBox.Show("请选择客户信息");
return;
}
//获取客户端IP地址
string ip = this.cboUsers.SelectedItem.ToString();
//发送信息
SocketSend[ip].Send(buffer);
}
客户端
public Form1()
{
InitializeComponent();
//取消跨线程检测
Control.CheckForIllegalCrossThreadCalls = false;
}
/// <summary>
/// 创建通信用线程
/// </summary>
Socket socketSend;
/// <summary>
/// 连接服务器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
//创建通信用的Socket
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取IP地址
IPAddress ip = IPAddress.Parse(this.txtServer.Text);
//获取端口号
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.txtPort.Text));
//连接服务器
socketSend.Connect(point);
ShowMessage("连接服务器成功......");
//创建线程来接收服务端发送信息
Thread th1 = new Thread(Rec);
//设置为后台线程
th1.IsBackground = true;
th1.Start();
}
/// <summary>
/// 一直循环接收服务器发送的信息
/// </summary>
private void Rec()
{
while (true)
{
try
{
//创建一个字节数组来接收服务器端发送的信息
byte[] buffer = new byte[1024 * 1024 * 5];
//接收服务器发送的信息
int r = socketSend.Receive(buffer);
//获取协议信息
byte b = buffer[0];
//根据协议解析接收到的信息
//说明发送的是文本
if (b == 0)
{
//把接收到的字节数组转换成字符串
string str = System.Text.Encoding.UTF8.GetString(buffer, 1, r - 1);
ShowMessage(str);
}
//说明发送的是文件
else if (b == 1)
{
//创建一个保存文本框
SaveFileDialog sfd = new SaveFileDialog();
//设置文本框的提示信息
sfd.Title = "请选择文件的保存路径";
//设置文件的保存类型
sfd.Filter = "图片|*.jpg|视频|*.mav|所有文件|*.*";
//设置文件保存的初始路径
sfd.InitialDirectory = @"C:\Users\Administrator\Desktop\test";
sfd.ShowDialog();
string path = sfd.FileName;
//创建流来保存数据
FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
fsWrite.Write(buffer, 1, r - 1);
ShowMessage("保存文件成功");
}
//说明发送的是震动
else if (b == 2)
{
ZD();
}
}
catch
{
}
}
}
/// <summary>
/// 震动
/// </summary>
private void ZD()
{
//这里应该先保存窗体当前的位置
//然后把节点增加n,减少n,重复n次
//之后把窗体还原到原先的位置
for (int i = 0; i < 200; i++)
{
this.Location = new Point(220, 220);
this.Location = new Point(420, 420);
}
}
/// <summary>
/// 界面显示信息
/// </summary>
/// <param name="p"></param>
private void ShowMessage(string str)
{
this.txtLog.AppendText(str + "\r\n");
}
/// <summary>
/// 发送信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
//需要发送的信息
string str = this.txtMsg.Text;
//将需要发送的数据转换为字节数组
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(str);
//发送信息到服务端
socketSend.Send(buffer);
}
PS:
客户端没有与其他客户端通信的功能
客户端没有发送文件的功能
协议这里只是很简单的堆文件和文本只做了区别,没有对文件格式做区分,这个需要完善协议才可以
这里咩有对大文件进行处理,大文件发送涉及断点重传的问题,会比较麻烦
在此基础上可以做个聊天小程序