1. 程序界面设计
大概设计一个如下的简单界面。
2. 各部分核心代码
2.1连接服务端
当请求连接按钮按下时,会触发点击事件,然后进行连接服务端请求。
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
if (IsConnected == false)
{
tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 创建一个Socket套接字
IPAddress ipaddress = IPAddress.Parse(cbbHostIP.Text);
EndPoint point = new IPEndPoint(ipaddress, int.Parse(tbHostPort.Text));
tcpClient.Connect(point);
tcpClient.BeginReceive(recieveBuffer, 0, recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
IsConnected = true;
btnConnect.Text = "断开连接";
}
else
{
tcpClient.Disconnect(false);
IsConnected = false;
btnConnect.Text = "请求连接";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
按下连接时,先创建一个socket套接字,然后从控件中获得用户输入的服务端IP和端口,然后进行连接。
最重要的是,这里进行了异步接收设计,设置了接收回调函数 ReceiveCallback。
2.2 发送数据
因为发送数据,是用户主动按下“发送”按钮发送的,所以比较简单,只要有按下发送按钮的点击事件,就发送数据。
private void btnSend_Click(object sender, EventArgs e)
{
if (IsConnected == true)
{
tcpClient.Send(tbSend.Text.GetBytes("GBK"));
}
else
{
MessageBox.Show("请先连接服务端");
}
}
2.3 接收数据
接收数据采用异步接收方式,因为无法确定服务端什么时候会发送数据给客户端。
private void ReceiveCallback(IAsyncResult AR)
{
// Check how much bytes are recieved and call EndRecieve to finalize handshake
try
{
int recieved = tcpClient.EndReceive(AR);
if (recieved <= 0)
return;
string message = Encoding.GetEncoding("GBK").GetString(recieveBuffer, 0, recieved); // 只将接收到的数据进行转化
Print(message);
// Start receiving again
tcpClient.BeginReceive(recieveBuffer, 0, recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
}
catch (SocketException ex)
{
MessageBox.Show(ex.ToString());
}
}
其中,ReceiveCallback 函数是接收的回调函数。如果有数据接收,那么真正接收数据的处理过程,是在这个回调函数完成的。
3. 整个工程源码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
namespace Net_assistance
{
public partial class Form1 : Form
{
public static Socket tcpClient;
bool IsConnected = false;
private byte[] recieveBuffer = new byte[8142];
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
}
private void Form1_Load(object sender, EventArgs e)
{
// 获取本地可用的IP地址
string strHostIP = Dns.GetHostName();
IPAddress[] ipadrlist = Dns.GetHostAddresses(strHostIP);
foreach (IPAddress ipaddr in ipadrlist)
{
if (ipaddr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
cbbHostIP.Items.Add(ipaddr.ToString());
}
cbbHostIP.Items.Add("127.0.0.1"); // 最后加上回环测试地址
cbbHostIP.SelectedIndex = 0;
tbHostPort.Text = "8080";
tbSend.Text = "hello world!";
}
private void Print(string msg)
{
tbRecv.AppendText($"[{DateTime.Now.ToString("yyy-MM-dd HH.mm.ss.fff")}] {msg}\r\n");
}
private void ReceiveCallback(IAsyncResult AR)
{
// Check how much bytes are recieved and call EndRecieve to finalize handshake
try
{
int recieved = tcpClient.EndReceive(AR);
if (recieved <= 0)
return;
string message = Encoding.GetEncoding("GBK").GetString(recieveBuffer, 0, recieved); // 只将接收到的数据进行转化
Print(message);
// Start receiving again
tcpClient.BeginReceive(recieveBuffer, 0, recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
}
catch (SocketException ex)
{
MessageBox.Show(ex.ToString());
}
}
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
if (IsConnected == false)
{
tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 创建一个Socket套接字
IPAddress ipaddress = IPAddress.Parse(cbbHostIP.Text);
EndPoint point = new IPEndPoint(ipaddress, int.Parse(tbHostPort.Text));
tcpClient.Connect(point);
tcpClient.BeginReceive(recieveBuffer, 0, recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
IsConnected = true;
btnConnect.Text = "断开连接";
}
else
{
tcpClient.Disconnect(false);
IsConnected = false;
btnConnect.Text = "请求连接";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void btnSend_Click(object sender, EventArgs e)
{
if (IsConnected == true)
{
tcpClient.Send(tbSend.Text.GetBytes("GBK"));
}
else
{
MessageBox.Show("请先连接服务端");
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (IsConnected == true)
{// 关闭窗口时,如果正在连接着服务端,那么断开连接后再退出
tcpClient.Disconnect(false);
}
}
}
}
4. 测试
先用网络调试助手打开一个服务端。如下:
然后打开我们自己写的客户端程序,输入服务端的IP和端口后,点击连接。这时可以看到服务端监测到有有客户端的接入了,这时就可以互相收发数据了,效果如下: