C# Socket服务端与客户端通信(包含大文件的断点传输)
步骤:
一、服务端的建立
1.服务端的项目建立以及页面布局
2.各功能按键的事件代码
1)传输类型说明以及全局变量
2)Socket通信服务端具体步骤:
(1)建立一个Socket
(2)接收信息
(3)发送数据(这里分发送字符串、文件(包含大文件)、震动)
二、客户端的建立
1.服务端的项目建立以及页面布局
2.各功能按键的事件代码
1)传输类型说明以及全局变量
2)Socket通信服务端具体步骤:
(1)建立一个Socket
(2)接收信息
(3)发送数据(这里分发送字符串、文件(包含大文件)、震动)
注意:此图是Socket通信的精华,在使用Socket通信时,有什么迷惑的可以看看此图,下面我们讲解的时候也是参照此图
Socket大家肯定很熟悉,对已内部的通信逻辑,肯定也有一定得了解---
对于Socket研究了两天写了一个小程序,通过Socket服务端与客户端的通信,以及大文件之间断点的传输(这里只做了服务端给客户端传送大文件,如果想把客户端的大文件传送给服务端也是一样的道理,看了文章,大家肯定可以自己实现)······
(自己才疏学浅,如有bug请谅解,但功能还是能实现的)
下面根据步骤进入正题:
一、服务端的建立
1.服务端的项目建立以及页面布局
新建解决方案“Socket通信”以及两个Winform项目(1)SockeClient——客户端 (2)SocketServer——服务器
给服务端界面布局——参照上图(这个大家肯定都是手到擒来就不累赘了······)
2.各功能按键的事件代码
先把整个服务端的代码贴出来,然后我们在一一讲解
namespace SocketServer
{
public partial class Form1 : Form
{ //说明:在传递信息的时候,会在需要传递的信息前面加一个字符来标识传递的是不同的信息
// 0:表示传递的是字符串信息
// 1:表示传递的是文件信息
// 2:表示的是震动 /// <summary>
/// 用来存放连接服务的客户端的IP地址和端口号,对应的Socket
/// </summary>
Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>(); public Form1()
{
InitializeComponent();
} private void Form1_Load(object sender, EventArgs e)
{
//不检测跨线程之间的空间调用
Control.CheckForIllegalCrossThreadCalls = false;
} /// <summary>
/// 开启监听
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
try
{
//当点击开始监听的时候 在服务器端创建一个负责监IP地址跟端口号的Socket
Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取IP
IPAddress ip = IPAddress.Any;
//创建端口号
IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
//监听
socketWatch.Bind(port);
ShowMsg("监听成功");
socketWatch.Listen();
//新建线程,去接收客户端发来的信息
Thread td = new Thread(AcceptMgs);
td.IsBackground = true;
td.Start(socketWatch);
}
catch
{ }
} /// <summary>
/// 接收客户端发送的信息
/// </summary>
/// <param name="o"></param>
private void AcceptMgs(object o)
{
try
{
Socket socketWatc = (Socket)o;
while (true)
{
////负责跟客户端通信的Socket
Socket socketSend = socketWatc.Accept();
//将远程连接的客户端的IP地址和Socket存入集合中
dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
//将远程连接的客户端的IP地址和端口号存储下拉框中
cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString());
ShowMsg(socketSend.RemoteEndPoint.ToString() + ": 连接成功");
//新建线程循环接收客户端发来的信息
Thread td = new Thread(Recive);
td.IsBackground = true;
td.Start(socketSend);
}
}
catch { } } /// <summary>
/// 接收客户端发来的数据,并显示出来
/// </summary>
private void Recive(object o)
{
Socket socketSend = (Socket)o;
try
{
while (true)
{
//客户端连接成功后,服务器应该接受客户端发来的消息 if (socketSend == null)
{
MessageBox.Show("请选择要发送的客户端");
continue;
}
byte[] buffer = new byte[ * * ];
//实际接受到的有效字节数
int r = socketSend.Receive(buffer);
//如果客户端关闭,发送的数据就为空,然后就跳出循环
if (r == )
{
break;
}
if (buffer[] == ) //如果接收的字节数组的第一个字节是0,说明接收的字符串信息
{
string strMsg = Encoding.UTF8.GetString(buffer, , r - );
ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
}
else if (buffer[] == ) //如果接收的字节数组的第一个字节是1,说明接收的是文件
{
string filePath = "";
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "保存文件";
sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
sfd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
//如果没有选择保存文件路径就一直打开保存框
while (true)
{
sfd.ShowDialog(this);
filePath = sfd.FileName;
if (string.IsNullOrEmpty(filePath))
{
continue;
}
else
{
break;
}
}
//保存接收的文件
using (FileStream fsWrite = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
fsWrite.Write(buffer, , r - );
}
ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功"); }
else if (buffer[] == ) //如果接收的字节数组的第一个字节是2,说明接收的是震动
{
ZD();
}
}
}
catch{}
} /// <summary>
/// 显示信息
/// </summary>
/// <param name="message"></param>
private void ShowMsg(string message)
{
txtLog.AppendText(message + "\r\n");
} /// <summary>
/// 发送信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{ //获得选中客户端ip对应的通信Socket
if (cboUsers.SelectedItem == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
if (socketSend == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
string strSend=txtMsg.Text;
try
{
byte[] buffer = Encoding.UTF8.GetBytes(strSend);
//获得发送的信息时候,在数组前面加上一个字节 0
List<byte> list = new List<byte>();
list.Add();
list.AddRange(buffer);
//将泛型集合转换为数组
byte[] newBuffer = list.ToArray();
//将了标识字符的字节数组传递给客户端
socketSend.Send(newBuffer);
txtMsg.Text = "";
}
catch
{
}
} /// <summary>
/// 选择文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelect_Click(object sender, EventArgs e)
{
//打开文件
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "选择要传的文件";
ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
ofd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
ofd.ShowDialog();
//得到选择文件的路径
txtPath.Text = ofd.FileName;
} /// <summary>
/// 发送文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendFile_Click(object sender, EventArgs e)
{
//判断是否选择了要发送的客户端
if (cboUsers.SelectedItem == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
if (socketSend == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
string filePath = txtPath.Text;
if (string.IsNullOrEmpty(filePath))
{
MessageBox.Show("请选择文件");
return;
}
Thread td = new Thread(SendBigFile);
td.IsBackground = true;
td.Start(); } /// <summary>
/// 大文件断点传送
/// </summary>
private void SendBigFile()
{
string filePath = txtPath.Text;
Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
try
{
//读取选择的文件
using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
{
//1. 第一步:发送一个包,表示文件的长度,让客户端知道后续要接收几个包来重新组织成一个文件
long length = fsRead.Length;
byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString());
//获得发送的信息时候,在数组前面加上一个字节 1
List<byte> list = new List<byte>();
list.Add();
list.AddRange(byteLength);
socketSend.Send(list.ToArray()); //
//2. 第二步:每次发送一个1MB的包,如果文件较大,则会拆分为多个包
byte[] buffer = new byte[ * ];
long send = ; //发送的字节数
while (true) //大文件断点多次传输
{
int r = fsRead.Read(buffer, , buffer.Length);
if (r == )
{
break;
}
socketSend.Send(buffer, , r, SocketFlags.None);
send += r;
ShowMsg(string.Format("{0}: 已发送:{1}/{2}", socketSend.RemoteEndPoint, send, length));
}
ShowMsg("发送完成");
txtPath.Text = "";
}
}
catch
{ }
} /// <summary>
/// 震动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnZD_Click(object sender, EventArgs e)
{
//判断是否选择了要发送的客户端
if (cboUsers.SelectedItem == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
if (socketSend == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
try
{
// 首字节是2说明是震动
byte[] buffer = new byte[];
buffer[] = ;
socketSend.Send(buffer);
}
catch
{ } } /// <summary>
/// 震动
/// </summary>
private void ZD()
{
//获取当前窗体的坐标
Point point = this.Location;
//反复给窗体坐标复制一百次,达到震动的效果
for (int i = ; i < ; i++)
{
this.Location = new Point(point.X - , point.Y - );
this.Location = new Point(point.X + , point.Y + );
}
this.Location = point;
}
}
}
1)传输类型说明以及全局变量
这些说明以及全局变量,说的也比较清楚,也不累赘了。
2)Socket通信服务端具体步骤:
(这些步骤都是根据第一个图来的)
(1)建立一个Socket
/// <summary>
/// 开启监听
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
try
{
//当点击开始监听的时候 在服务器端创建一个负责监IP地址跟端口号的Socket
Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取IP
IPAddress ip = IPAddress.Any;
//创建端口号
IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
//监听
socketWatch.Bind(port);
ShowMsg("监听成功");
socketWatch.Listen();
//新建线程,去接收客户端发来的信息
Thread td = new Thread(AcceptMgs);
td.IsBackground = true;
td.Start(socketWatch);
}
catch
{ }
}
在开启监听按钮里,我们建立了Socket,以及监听的最大客户端数 socketWatch.Listen(10)
由于服务端会不停的去监视接收客户端发来的信息,如果把这个工作放到主线程里,程序会出现假死的现象,所以这里给他放到一个新的线程里。
(2)接收信息
/// <summary>
/// 接收客户端发送的信息
/// </summary>
/// <param name="o"></param>
private void AcceptMgs(object o)
{
try
{
Socket socketWatc = (Socket)o;
while (true)
{
////负责跟客户端通信的Socket
Socket socketSend = socketWatc.Accept();
//将远程连接的客户端的IP地址和Socket存入集合中
dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
//将远程连接的客户端的IP地址和端口号存储下拉框中
cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString());
ShowMsg(socketSend.RemoteEndPoint.ToString() + ": 连接成功");
//新建线程循环接收客户端发来的信息
Thread td = new Thread(Recive);
td.IsBackground = true;
td.Start(socketSend);
}
}
catch { } }
接收信息是会根据接收到字节数字的第一个字节来判断接收到的是什么
这个在方法Recive里进行判断
/// <summary>
/// 接收客户端发来的数据,并显示出来
/// </summary>
private void Recive(object o)
{
Socket socketSend = (Socket)o;
try
{
while (true)
{
//客户端连接成功后,服务器应该接受客户端发来的消息 if (socketSend == null)
{
MessageBox.Show("请选择要发送的客户端");
continue;
}
byte[] buffer = new byte[ * * ];
//实际接受到的有效字节数
int r = socketSend.Receive(buffer);
//如果客户端关闭,发送的数据就为空,然后就跳出循环
if (r == )
{
break;
}
if (buffer[] == ) //如果接收的字节数组的第一个字节是0,说明接收的字符串信息
{
string strMsg = Encoding.UTF8.GetString(buffer, , r - );
ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
}
else if (buffer[] == ) //如果接收的字节数组的第一个字节是1,说明接收的是文件
{
string filePath = "";
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "保存文件";
sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
sfd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
//如果没有选择保存文件路径就一直打开保存框
while (true)
{
sfd.ShowDialog(this);
filePath = sfd.FileName;
if (string.IsNullOrEmpty(filePath))
{
continue;
}
else
{
break;
}
}
//保存接收的文件
using (FileStream fsWrite = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
fsWrite.Write(buffer, , r - );
}
ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功"); }
else if (buffer[] == ) //如果接收的字节数组的第一个字节是2,说明接收的是震动
{
ZD();
}
}
}
catch{}
}
(3)发送数据(这里分发送字符串、文件(包含大文件)、震动)
发送字符串信息
/// <summary>
/// 发送信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{ //获得选中客户端ip对应的通信Socket
if (cboUsers.SelectedItem == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
if (socketSend == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
string strSend=txtMsg.Text;
try
{
byte[] buffer = Encoding.UTF8.GetBytes(strSend);
//获得发送的信息时候,在数组前面加上一个字节 0
List<byte> list = new List<byte>();
list.Add();
list.AddRange(buffer);
//将泛型集合转换为数组
byte[] newBuffer = list.ToArray();
//将了标识字符的字节数组传递给客户端
socketSend.Send(newBuffer);
txtMsg.Text = "";
}
catch
{
}
}
发送震动
/// <summary>
/// 震动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnZD_Click(object sender, EventArgs e)
{
//判断是否选择了要发送的客户端
if (cboUsers.SelectedItem == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
if (socketSend == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
try
{
// 首字节是2说明是震动
byte[] buffer = new byte[];
buffer[] = ;
socketSend.Send(buffer);
}
catch
{ } } /// <summary>
/// 震动
/// </summary>
private void ZD()
{
//获取当前窗体的坐标
Point point = this.Location;
//反复给窗体坐标复制一百次,达到震动的效果
for (int i = ; i < ; i++)
{
this.Location = new Point(point.X - , point.Y - );
this.Location = new Point(point.X + , point.Y + );
}
this.Location = point;
}
发送文件(包含大文件)
首先要选择文件
/// <summary>
/// 选择文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelect_Click(object sender, EventArgs e)
{
//打开文件
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "选择要传的文件";
ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
ofd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
ofd.ShowDialog();
//得到选择文件的路径
txtPath.Text = ofd.FileName;
}
然后在发送文件
/// <summary>
/// 发送文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendFile_Click(object sender, EventArgs e)
{
//判断是否选择了要发送的客户端
if (cboUsers.SelectedItem == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
if (socketSend == null)
{
MessageBox.Show("请选择要发送的客户端");
return;
}
string filePath = txtPath.Text;
if (string.IsNullOrEmpty(filePath))
{
MessageBox.Show("请选择文件");
return;
}
Thread td = new Thread(SendBigFile);
td.IsBackground = true;
td.Start(); } /// <summary>
/// 大文件断点传送
/// </summary>
private void SendBigFile()
{
string filePath = txtPath.Text;
Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
try
{
//读取选择的文件
using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
{
//1. 第一步:发送一个包,表示文件的长度,让客户端知道后续要接收几个包来重新组织成一个文件
long length = fsRead.Length;
byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString());
//获得发送的信息时候,在数组前面加上一个字节 1
List<byte> list = new List<byte>();
list.Add();
list.AddRange(byteLength);
socketSend.Send(list.ToArray()); //
//2. 第二步:每次发送一个4KB的包,如果文件较大,则会拆分为多个包
byte[] buffer = new byte[ * ];
long send = ; //发送的字节数
while (true) //大文件断点多次传输
{
int r = fsRead.Read(buffer, , buffer.Length);
if (r == )
{
break;
}
socketSend.Send(buffer, , r, SocketFlags.None);
send += r;
ShowMsg(string.Format("{0}: 已发送:{1}/{2}", socketSend.RemoteEndPoint, send, length));
}
ShowMsg("发送完成");
txtPath.Text = "";
}
}
catch
{ }
}
注意:(1)发送文件的时候会分两步发送 :第一步:发送一个包,表示文件的长度,让客户端知道后续要接收几个包来重新组织成一个文件
第二步:每次发送一个1MB的包,如果文件较大,则会拆分为多个包
(2)每个客户端连接服务端的啥时候,都会把客户端的ip以及端口号,放到下拉框里,想给那个客户端发信息,就选择对应的客户端
二、客户端的建立
1.客户端的项目建立以及页面布局
客户端的界面布局与服务端很像,就是把对应的开始监听换成连接,当然代码也会有所改变,后面会讲到·····
2.各功能按键的事件代码
先把整个服客户端的代码贴出来,然后我们在一一讲解
namespace SocketClient
{
public partial class Form1 : Form
{ //说明:在传递信息的时候,会在需要传递的信息前面加一个字符来标识传递的是不同的信息
// 0:表示传递的是字符串信息
// 1:表示传递的是文件信息
// 2:表示的是震动 /// <summary>
/// 用来存放连接服务的IP地址和端口号,对应的Socket (这个为了以后的扩展用,现在暂时没用)
/// </summary>
Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>(); /// <summary>
/// 存储保存文件的路径
/// </summary>
string filePath = "";
/// <summary>
/// 负责通信的Socket
/// </summary>
Socket socketSend; public Form1()
{
InitializeComponent();
} private void Form1_Load(object sender, EventArgs e)
{
//不检测跨线程之间的空间调用
Control.CheckForIllegalCrossThreadCalls = false;
} /// <summary>
/// 建立连接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
try
{
//创建负责通信的Socket
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取服务端的IP
IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
//获取服务端的端口号
IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
//获得要连接的远程服务器应用程序的IP地址和端口号
socketSend.Connect(port);
ShowMsg("连接成功");
//新建线程,去接收客户端发来的信息
Thread td = new Thread(AcceptMgs);
td.IsBackground = true;
td.Start();
}
catch { }
} /// <summary>
/// 接收数据
/// </summary>
private void AcceptMgs()
{
try
{
/// <summary>
/// 存储大文件的大小
/// </summary>
long length = ;
long recive = ; //接收的大文件总的字节数
while (true)
{
byte[] buffer = new byte[ * ];
int r = socketSend.Receive(buffer);
if (r == )
{
break;
}
if (length > ) //判断大文件是否已经保存完
{
//保存接收的文件
using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write))
{
fsWrite.Write(buffer, , r);
length -= r; //减去每次保存的字节数
ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));
if (length <= )
{
ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
}
continue;
}
}
if (buffer[] == ) //如果接收的字节数组的第一个字节是0,说明接收的字符串信息
{
string strMsg = Encoding.UTF8.GetString(buffer, , r - );
ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
}
else if (buffer[] == ) //如果接收的字节数组的第一个字节是1,说明接收的是文件
{
length = int.Parse(Encoding.UTF8.GetString(buffer,,r-));
recive = length;
filePath = "";
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "保存文件";
sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
sfd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
//如果没有选择保存文件路径就一直打开保存框
while (true)
{
sfd.ShowDialog(this);
filePath = sfd.FileName;
if (string.IsNullOrEmpty(filePath))
{
continue;
}
else
{
break;
}
}
}
else if (buffer[] == ) //如果接收的字节数组的第一个字节是2,说明接收的是震动
{
ZD();
}
}
}
catch { } } /// <summary>
/// 显示信息
/// </summary>
/// <param name="message"></param>
private void ShowMsg(string message)
{
txtLog.AppendText(message + "\r\n");
} /// <summary>
/// 发送数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
try
{
byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
//获得发送的信息时候,在数组前面加上一个字节 0
List<byte> list = new List<byte>();
list.Add();
list.AddRange(buffer);
//将泛型集合转换为数组
byte[] newBuffer = list.ToArray();
//将了标识字符的字节数组传递给客户端
socketSend.Send(newBuffer);
txtMsg.Text = "";
}
catch{}
} /// <summary>
/// 选择文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelect_Click(object sender, EventArgs e)
{
//打开文件
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "选择要传的文件";
ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
ofd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
ofd.ShowDialog();
//得到选择文件的路径
txtPath.Text = ofd.FileName;
} /// <summary>
/// 发送文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendFile_Click(object sender, EventArgs e)
{
try
{
string filePath = txtPath.Text;
if (string.IsNullOrEmpty(filePath))
{
MessageBox.Show("请选择文件");
return;
}
//读取选择的文件
using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
{
byte[] buffer = new byte[ * * ];
int r = fsRead.Read(buffer, , buffer.Length);
//获得发送的信息时候,在数组前面加上一个字节 1
List<byte> list = new List<byte>();
list.Add();
list.AddRange(buffer);
byte[] newBuffer = list.ToArray();
//将了标识字符的字节数组传递给客户端
socketSend.Send(newBuffer, , r + , SocketFlags.None);
txtPath.Text = "";
}
}
catch{ }
} /// <summary>
/// 震动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnZD_Click(object sender, EventArgs e)
{
try
{
// 首字节是2说明是震动
byte[] buffer = new byte[];
buffer[] = ;
socketSend.Send(buffer);
}
catch{ }
} /// <summary>
/// 震动
/// </summary>
private void ZD()
{
//获取当前窗体的坐标
Point point = this.Location;
//反复给窗体坐标复制一百次,达到震动的效果
for (int i = ; i < ; i++)
{
this.Location = new Point(point.X - , point.Y - );
this.Location = new Point(point.X + , point.Y + );
}
this.Location = point;
}
}
}
1)传输类型说明以及全局变量
这些说明以及全局变量,说的也比较清楚,也不累赘了。
2)Socket通信服务端具体步骤:
(这些步骤都是根据第一个图来的)
(1)建立一个通信的Socket
/// <summary>
/// 建立连接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
try
{
//创建负责通信的Socket
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取服务端的IP
IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
//获取服务端的端口号
IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
//获得要连接的远程服务器应用程序的IP地址和端口号
socketSend.Connect(port);
ShowMsg("连接成功");
//新建线程,去接收客户端发来的信息
Thread td = new Thread(AcceptMgs);
td.IsBackground = true;
td.Start();
}
catch { }
}
在连接按钮里,我们建立了Socket
由于客户端会不停的去监视接收服务端发来的信息,如果把这个工作放到主线程里,程序会出现假死的现象,所以这里给他放到一个新的线程里。
(2)接收信息
/// <summary>
/// 接收数据
/// </summary>
private void AcceptMgs()
{
try
{
/// <summary>
/// 存储大文件的大小
/// </summary>
long length = ;
long recive = ; //接收的大文件总的字节数
while (true)
{
byte[] buffer = new byte[ * ];
int r = socketSend.Receive(buffer);
if (r == )
{
break;
}
if (length > ) //判断大文件是否已经保存完
{
//保存接收的文件
using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write))
{
fsWrite.Write(buffer, , r);
length -= r; //减去每次保存的字节数
ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));
if (length <= )
{
ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
}
continue;
}
}
if (buffer[] == ) //如果接收的字节数组的第一个字节是0,说明接收的字符串信息
{
string strMsg = Encoding.UTF8.GetString(buffer, , r - );
ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
}
else if (buffer[] == ) //如果接收的字节数组的第一个字节是1,说明接收的是文件
{
length = int.Parse(Encoding.UTF8.GetString(buffer,,r-));
recive = length;
filePath = "";
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "保存文件";
sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
sfd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
//如果没有选择保存文件路径就一直打开保存框
while (true)
{
sfd.ShowDialog(this);
filePath = sfd.FileName;
if (string.IsNullOrEmpty(filePath))
{
continue;
}
else
{
break;
}
}
}
else if (buffer[] == ) //如果接收的字节数组的第一个字节是2,说明接收的是震动
{
ZD();
}
}
}
catch { } }
接收信息是会根据接收到字节数字的第一个字节来判断接收到的是什么,如果接收的是个大文件,首先会接收大文件的大小,然后根据大小接收相同大小的字节数组追加保存到一个文件里去。
(3)发送数据(这里分发送字符串、文件(包含大文件)、震动)
发送字符串信息
/// <summary>
/// 发送数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
try
{
byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
//获得发送的信息时候,在数组前面加上一个字节 0
List<byte> list = new List<byte>();
list.Add();
list.AddRange(buffer);
//将泛型集合转换为数组
byte[] newBuffer = list.ToArray();
//将了标识字符的字节数组传递给客户端
socketSend.Send(newBuffer);
txtMsg.Text = "";
}
catch{}
}
发送震动
/// <summary>
/// 震动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnZD_Click(object sender, EventArgs e)
{
try
{
// 首字节是2说明是震动
byte[] buffer = new byte[];
buffer[] = ;
socketSend.Send(buffer);
}
catch{ }
} /// <summary>
/// 震动
/// </summary>
private void ZD()
{
//获取当前窗体的坐标
Point point = this.Location;
//反复给窗体坐标复制一百次,达到震动的效果
for (int i = ; i < ; i++)
{
this.Location = new Point(point.X - , point.Y - );
this.Location = new Point(point.X + , point.Y + );
}
this.Location = point;
}
发送文件(不包含大文件)
首先要选择文件
/// <summary>
/// 选择文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelect_Click(object sender, EventArgs e)
{
//打开文件
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "选择要传的文件";
ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
ofd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
ofd.ShowDialog();
//得到选择文件的路径
txtPath.Text = ofd.FileName;
}
然后在发送文件
/// <summary>
/// 发送文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendFile_Click(object sender, EventArgs e)
{
try
{
string filePath = txtPath.Text;
if (string.IsNullOrEmpty(filePath))
{
MessageBox.Show("请选择文件");
return;
}
//读取选择的文件
using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
{
byte[] buffer = new byte[ * * ];
int r = fsRead.Read(buffer, , buffer.Length);
//获得发送的信息时候,在数组前面加上一个字节 1
List<byte> list = new List<byte>();
list.Add();
list.AddRange(buffer);
byte[] newBuffer = list.ToArray();
//将了标识字符的字节数组传递给客户端
socketSend.Send(newBuffer, , r + , SocketFlags.None);
txtPath.Text = "";
}
}
catch{ }
}
到此,客户端以及服务端都写好了,就可以启动客户端和服务端进行通信了,由于我是在一个电脑上模拟,就用的是相同的ip,如:
至此,整个Socket的通信就结束了,如有什么建议希望不吝赐教·····大家一起学习······
C# Socket服务端与客户端通信(包含大文件的断点传输)的更多相关文章
- C#Winform窗体实现服务端和客户端通信例子(TCP/IP)
Winform窗体实现服务端和客户端通信的例子,是参考这个地址 http://www.cnblogs.com/longwu/archive/2011/08/25/2153636.html 进行了一些异 ...
- Netty 学习(二):服务端与客户端通信
Netty 学习(二):服务端与客户端通信 作者: Grey 原文地址: 博客园:Netty 学习(二):服务端与客户端通信 CSDN:Netty 学习(二):服务端与客户端通信 说明 Netty 中 ...
- (C#:Socket)简单的服务端与客户端通信。
要求:1.可以完成一对一的通信:2.实现服务端对客户端一对多的选择发送:3.可以实现服务端的群发功能:4.可以实现客户端文件的发送: 要点:服务器端:第一步:用指定的端口号和服务器的ip建立一个End ...
- Python3学习之路~8.3 socket 服务端与客户端
通过8.2的实例1-6,我们可以总结出来,socket的服务端和客户端的一般建立步骤: 服务端 步骤:1创建实例,2绑定,3监听,4阻塞,5发送&接收数据,6关闭. #Author:Zheng ...
- socket系列之socket服务端与客户端如何通信
上面已经分别介绍了ServerSocket跟Socket的工作步骤,并且从应用层往系统底层剖析其运作原理,我们清楚了他们各自的一块,现在我们将把他们结合起来,看看他们是如何通信的,并详细讨论一下他们之 ...
- PHP socket服务端与客户端的简易通信
今天学习socket通信的同时,顺便整理了下以前初识socket的知识. 现在关于php的socket通信,有些框架已经十分成熟了,比如 swoole 和 workerman,这两个大家可以学习学习 ...
- java网络编程-单线程服务端与客户端通信
该服务器一次只能处理一个客户端请求;p/** * 利用Socket进行简单服务端与客户端连接 * 这是服务端 */public class EchoServer { private ServerSoc ...
- java的服务端与客户端通信(2)
一.Socket连接与HTTP连接 1.1Socket套接字 套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元.它是网络通信过程中端点的抽象表示,包含进行网络通信 ...
- java的服务端与客户端通信(1)
一.理解socket 1.1什么是socket? socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过"套接字"向网络 ...
随机推荐
- 成都Uber优步司机奖励政策(3月8日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- 苏州Uber优步司机奖励政策(12月14日到12月20日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- Mysql 8.0.* zip版本 windows安装
一,MySQL8.0.*zip版本安装步骤. 1,下载 https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.15-winx64.zip 注现 ...
- Spring的定时任务(任务调度)<task:scheduled-tasks>
Spring内部有一个task是Spring自带的一个设定时间自动任务调度,提供了两种方式进行配置,一种是注解的方式,而另外一种就是XML配置方式了.注解方式比较简洁,XML配置方式相对而言有些繁琐, ...
- 开发发布npm module包
开发发布npm module包 问题 在项目开发过程中,每当进入一个新的业务项目,从零开始搭建一套前端项目结构是一件让人头疼的事情,就要重新复制一个上一个项目的前端框架和组件代码库.其中很多功能的模块 ...
- 用最简单的MVC模式输出内容
MVC是模型(model)-视图(view)-控制器(controller)的缩写,它的作用是使代码分离,可维护性高.重用性高 编写Model层: <?php class model{ publ ...
- 前端开发工程师 - 02.JavaScript程序设计 - 期末考试
期末考试客观题 期末考试主观题 https://www.15yan.com/story/aY0HWAQ7oNU/ 1(8分) 函数myType用于根据输入参数返回相应的类型信息. 语法如下: ...
- 利用maven进行项目管理
下面为maven项目管理的一个结构 首先pom是路径文件,我们在编译或是运行程序时调用到jdk或一些自己写的jar包时会需要指明物理路径,这里的pom是一样的道理,同时在maven的管理下多出来了一些 ...
- isX字符串方法
islower():返回True,如果字符串至少有一个字母,并且所有字母都是小写: 例如:>>> spam='Hello world' >>> spam.islow ...
- WCF服务库创建-20140919
1. 创建wcf服务库 2. 宿主到web程序上 // 宿主wcf服务库 RouteTable.Routes.Add(new ServiceRoute("ctserver.dll" ...