一.在我们讲解服务端编写之前,同样来看看该图。我们编写步骤,依此图为准。
根据我们之前的说明Socket是一个网络编程接口,那么我们就得根据此特性去使用它,Socket的作者也是依据网络通信的协议进行的接口编写,所以使用思路也是做的一个继承吧。
二. 开始上干货 我们来编写吧
思路大纲:
1.首先我们需要创建一个和服务端通信的的Socket,该Socket任务是和服务端连接后进行通信,他是一个Tcp/Udp客户器。步骤:设置需要连接服务端的IP和端口, 启动连接服务端服务。----这段描述,对比上图,按照顺序走过来初始化Socket对象-->Connet()。
2.客户端和服务端连接之后呢,我们就能够和服务端进行通信了,我们使用Socket对象下的Send方法,就能够发送消息给服务器。
3.当然客户端和服务器连接之后呢,服务器也能给客户端发送消息,所以我们使用Receive来接收服务器发送的消息。
到此大家体会一下。我们开始编写。
1.连接服务端代码
2.接收消息方法
3.发送消息方法
4.窗口关闭事件
三.上述代码界面:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Client
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//Socket对象
Socket sockClient = null;
//接收线程
Thread thrClient = null;
/// <summary>
/// 连接服务端
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnCon_Click(object sender, RoutedEventArgs e)
{
//定义要连接Tcp服务器的的IP和端口号。
IPAddress address = IPAddress.Parse(this.TxtIp.Text.Trim());
IPEndPoint Ipe = new IPEndPoint(address, int.Parse(this.TxtPort.Text.Trim()));
//创建一个用于和服务端通信的Socket对象(和服务端创建方式相同)
sockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
//编写界面提示
this.InfoMessage.Dispatcher.Invoke(new Action(() =>
{
this.InfoMessage.AppendText("与服务器连接中......" + Environment.NewLine);
}));
//传入网络地址信息参数,开始尝试和服务端建立连接
sockClient.Connect(Ipe);
}
catch (Exception ex)
{
MessageBox.Show("连接失败" + ex.Message, "建立连接");
return;
}
//编写界面提示
this.InfoMessage.Dispatcher.Invoke(new Action(() =>
{
this.InfoMessage.AppendText("与服务器连接成功" + Environment.NewLine);
}));
//开启一个线程用于接收服务端传递过来的消息
thrClient = new Thread(ReceiceMsg);
thrClient.IsBackground = true;
thrClient.Start();
}
#region 接收消息
/// <summary>
/// 接收消息
/// </summary>
private void ReceiceMsg()
{
while (true)
{
//定义一个2M缓冲区
byte[] arrMsgRec = new byte[1024 * 1024 * 2];
int length = -1;
try
{
length = sockClient.Receive(arrMsgRec);
}
catch (SocketException se) //服务端和客户端主动断开连接进入的异常
{
this.InfoMessage.Dispatcher.Invoke(new Action(() =>
{
this.InfoMessage.AppendText("断开连接:" + se.Message + Environment.NewLine);
}));
break;
}
catch (Exception ex)//其他方式异常处理
{
this.InfoMessage.Dispatcher.Invoke(new Action(() =>
{
this.InfoMessage.AppendText("断开连接" + ex.Message + Environment.NewLine);
}));
break;
}
if (length > 0)
{
//把服务端发送过来的二进制数据解码为字符串数据。
string strMsg = Encoding.UTF8.GetString(arrMsgRec, 0, length);
//去界面展示出来
string Msg = "[接收] " + strMsg + Environment.NewLine;
this.InfoMessage.Dispatcher.Invoke(new Action(() =>
{
this.InfoMessage.AppendText(Msg+Environment.NewLine);
}));
}
}
}
#endregion
#region 窗体关闭事件
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
sockClient?.Close();
}
#endregion
#region 发送消息
private void BtnSend_Click(object sender, RoutedEventArgs e)
{
//组合要发送的消息
string strMsg = "来自" + this.TxtName.Text.Trim() + ": " + this.TxtMessage.Text.Trim();
//编码为二进制数据,为发送给服务端做准备
byte[] arrMsg = Encoding.UTF8.GetBytes(strMsg);
//针对消息进行个性化处理,比如消息类型判断的个性化处理,为我们后面拓展消息做准备,目前我们这里不做处理,你们可以当成这里是多余的一步。
byte[] arrSend = new byte[arrMsg.Length];
//把arrMsg的消息复制到arrSend里面去。 参数说明:1.被复制的数组 2.被复制数组的开始索引 2.复制终点数组 4.开始索引
Buffer.BlockCopy(arrMsg, 0, arrSend, 0, arrMsg.Length);
//发送数据
sockClient.Send(arrSend);
this.InfoMessage.Dispatcher.Invoke(new Action(() =>
{
this.InfoMessage.AppendText("[发送] " + this.TxtMessage.Text.Trim() + Environment.NewLine);
}));
}
#endregion
private void BtnConClose_Click(object sender, RoutedEventArgs e)
{
sockClient?.Close();
}
}
}