步骤:

一、服务端的建立

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服务端与客户端通信(包含大文件的断点传输)的更多相关文章

  1. C#Winform窗体实现服务端和客户端通信例子(TCP/IP)

    Winform窗体实现服务端和客户端通信的例子,是参考这个地址 http://www.cnblogs.com/longwu/archive/2011/08/25/2153636.html 进行了一些异 ...

  2. Netty 学习(二):服务端与客户端通信

    Netty 学习(二):服务端与客户端通信 作者: Grey 原文地址: 博客园:Netty 学习(二):服务端与客户端通信 CSDN:Netty 学习(二):服务端与客户端通信 说明 Netty 中 ...

  3. (C#:Socket)简单的服务端与客户端通信。

    要求:1.可以完成一对一的通信:2.实现服务端对客户端一对多的选择发送:3.可以实现服务端的群发功能:4.可以实现客户端文件的发送: 要点:服务器端:第一步:用指定的端口号和服务器的ip建立一个End ...

  4. Python3学习之路~8.3 socket 服务端与客户端

    通过8.2的实例1-6,我们可以总结出来,socket的服务端和客户端的一般建立步骤: 服务端 步骤:1创建实例,2绑定,3监听,4阻塞,5发送&接收数据,6关闭. #Author:Zheng ...

  5. socket系列之socket服务端与客户端如何通信

    上面已经分别介绍了ServerSocket跟Socket的工作步骤,并且从应用层往系统底层剖析其运作原理,我们清楚了他们各自的一块,现在我们将把他们结合起来,看看他们是如何通信的,并详细讨论一下他们之 ...

  6. PHP socket服务端与客户端的简易通信

    今天学习socket通信的同时,顺便整理了下以前初识socket的知识. 现在关于php的socket通信,有些框架已经十分成熟了,比如  swoole 和 workerman,这两个大家可以学习学习 ...

  7. java网络编程-单线程服务端与客户端通信

    该服务器一次只能处理一个客户端请求;p/** * 利用Socket进行简单服务端与客户端连接 * 这是服务端 */public class EchoServer { private ServerSoc ...

  8. java的服务端与客户端通信(2)

    一.Socket连接与HTTP连接   1.1Socket套接字 套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元.它是网络通信过程中端点的抽象表示,包含进行网络通信 ...

  9. java的服务端与客户端通信(1)

    一.理解socket 1.1什么是socket? socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过"套接字"向网络 ...

随机推荐

  1. LeetCode: 57. Insert Interval(Hard)

    1. 原题链接 https://leetcode.com/problems/insert-interval/description/ 2. 题目要求 该题与上一题的区别在于,插入一个新的interva ...

  2. java nio之channel

    一.通道(Channel):由 java.nio.channels 包定义的.Channel 表示 IO 源与目标打开的连接.Channel 类似于传统的“流”.只不过 Channel本身不能直接访问 ...

  3. AT+CGDCONT=0,"IP","ctnb"设置问题

    发现有的时候,设置不成功,经过验证正确的方法是,模组刚上电,或者刚复位的时候,先发送AT+CFUN=1,然后再去设置APN AT+CFUN= OK AT+CGDCONT=,"IP" ...

  4. php安全性问题

    目录 常见攻击类型 1.sql注入: 2.xss攻击 3.csrf攻击: php安全三板斧:过滤输入.验证数据,以及转义输出. 1.数据过滤: 2.验证数据: 3.转义输出: laravel 中如何避 ...

  5. 限时购校验小工具&dubbo异步调用实现限

    本文来自网易云社区 作者:张伟 背景 限时购是网易考拉目前比较常用的促销形式,但是前期创建一个限时购活动时需要各个BU按照指定的Excel格式进行选品提报,为了保证提报数据准确,运营需要人肉校验很多信 ...

  6. outer join test

    create table t1_outerjoin(a int, b int , c int); create table t2_outerjoin(a int); create table t3_o ...

  7. 180609-Spring之事件驱动机制的简单使用

    文章链接:https://liuyueyi.github.io/hexblog/hexblog/2018/06/09/180609-Spring之事件驱动机制的简单使用/ Spring之事件驱动机制的 ...

  8. <cfenv>(fenv.h) _c++11

    头文件 <cfenv>(fenv.h) c++11 浮点环境 这个头文件声明了一系列的函数和宏去访问浮点环境,以及特殊的类型. 浮点环境维护一系列的状态标志(status flags)和具 ...

  9. JAVA Map 之元素定位,冲突碰撞

    基本特性: 维持健值对的集合接口,健不可以重复,每一个健只能映射到一个值. Map替代了原来的虚拟类Directory. Map提供了三种集合视角,keys(KeySet),values(Values ...

  10. Mongo DB Java操作

    1.首先下载Mongo DB java 驱动 2.操作Mongo 增删改查 package com.sjjy.mongo; import java.util.ArrayList;import java ...