对应上篇文章的服务端,和之前一个tcp异步通讯方法大致一致,不过这个相当于是单独的客户端通讯。
所实现的效果,左上添加客户端,添加后的客户端会追加到下拉框列表中,切换不同的下拉框对应不同的服务端通讯
实现客户端tcp通讯
#region 变量及属性
/// <summary>
/// 确保id唯一
/// </summary>
int comid = 0;
/// <summary>
/// 客户端连接实例
/// </summary>
private Dictionary<int, ClientMess> comlistDic=new Dictionary<int, ClientMess>();
/// <summary>
/// 绑定下拉框
/// </summary>
private Dictionary<int,string> comListstringDic=new Dictionary<int,string>();
//当前下拉框选择的类
ClientMess NowClientClass=new ClientMess();/// <summary>
/// 客户端连接信息类
/// </summary>
public class ClientMess
{
public int CComid=0;
public string CIp="";
public string CPort="";
public Socket CSocket=null;
public bool CIfConn=false;
public string CRecvStr="";
}
public class StateObject
{
// 当前客户端的Socket
public Socket workSocket = null;
// 可接收的最大字节数
public const int BufferSize = 1024 * 1024;
// 接收的数据存储
public byte[] buffer = new byte[BufferSize];
}//内存缓存区
public static Socket client;
public static IPEndPoint serverPoint = null;
#endregion
/// <summary>
/// 连接服务端
/// </summary>
/// <param name="ip">Ip</param>
/// <param name="port">端口</param>
public bool ConnectServercer(string ip, string port)
{
IPAddress IP = IPAddress.Parse(ip.Trim());
serverPoint = new IPEndPoint(IP, Convert.ToInt32(port));
try
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.BeginConnect(serverPoint, new AsyncCallback(ConnectCallback), client);
Thread.Sleep(500);//异步等500毫秒返回连接结果
return client.Connected;
}
catch (Exception ex)
{
MessageBox.Show("异常-客户端开启连接", client.LocalEndPoint + "异常:【" + ex.Message + "】");
return false;
}
}private void ConnectCallback(IAsyncResult ar) //连接客户端异步回调
{
client = (Socket)ar.AsyncState;
try
{
if (client != null && client.Connected)
{
client.EndConnect(ar);
Receive(client);
NowClientClass.CSocket = client;
NowClientClass.CRecvStr+= (DateTime.Now + client.LocalEndPoint.ToString() + " 成功连接服务端:" + client.RemoteEndPoint.ToString() + "\r\n");
NowClientClass.CIfConn = true;
this.Invoke(new Action(() =>//线程间操作无效: 从不是创建控件的线程访问它,使用委托
{
button6.Enabled = false;//连接按钮
button7.Enabled = true;//断开按钮
textBox7.Text = NowClientClass.CRecvStr;//接收区
label6.Text = client.LocalEndPoint.ToString();//本地ip端口就
}));
}
else
{
MessageBox.Show("服务端未开启");
client.Close();
}
}
catch (Exception ex)
{
MessageBox.Show("异常-客户端连接回调" + client.LocalEndPoint, "异常连接" + serverPoint + "【" + ex.Message + "】");
}
}//接收消息
private void Receive(Socket client)
{
StateObject state = new StateObject();
state.workSocket = client;
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
//接收消息异步回调
private void ReceiveCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
client = state.workSocket;
EndPoint endpoint = null;
try
{
if (client != null && client.Connected)
{
endpoint = client.LocalEndPoint;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)//读取到服务端发送过来的字节长度
{
byte[] ActConn = new byte[bytesRead];
Buffer.BlockCopy(state.buffer, 0, ActConn, 0, bytesRead);//将接收到的byte复制接收到的字节长度到新的byte集合中
this.Invoke(new Action(() =>
{
if (radioButton7.Checked)//hex接收
{
NowClientClass.CRecvStr +=(DateTime.Now + " 【接收】" + client.RemoteEndPoint + "发送的数据 :"+ BitConverter.ToString(ActConn, 0, ActConn.Length).Replace("-", "") + "\r\n");
}
else//ascll
{
NowClientClass.CRecvStr += (DateTime.Now + " 【接收】" + client.RemoteEndPoint + "发送的数据 :" + Encoding.Default.GetString(ActConn) + "\r\n");
}
textBox7.Text = NowClientClass.CRecvStr;
}));
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);//继续接收数据
}
else
{
if (client != null && client.Connected)
{
client.Close();//关闭连接
}
this.Invoke(new Action(() =>//异步中操作控件报错:线程间操作无效: 从不是创建控件的线程访问它 使用委托
{button6.Enabled = true;//连接按钮
button7.Enabled = false;//断开按钮
NowClientClass.CIfConn = false;//当前客户端连接状态
NowClientClass.CRecvStr +=( DateTime.Now + " 【接收】服务端发送的空字符串:服务端断开连接" + "\r\n");
textBox7.Text = NowClientClass.CRecvStr;
}));
}
}
else
{
client.Close();
this.Invoke(new Action(() =>
{button6.Enabled = true;
button7.Enabled = false;
NowClientClass.CIfConn = false;
string aa = NowClientClass.CRecvStr + DateTime.Now + " 【接收】服务端发送的信息:服务端断开连接" + "\r\n";
NowClientClass.CRecvStr = aa;
textBox7.Text = aa;
}));
//服务端断开连接
}
}
catch (Exception ex)
{
if (client != null && client.Connected)
{
client.Close();
}
if (endpoint != null)
{
MessageBox.Show("异常-客户端接收消息回调" + endpoint, serverPoint + "异常【" + ex.Message + "】");
}
else
{
MessageBox.Show("异常-客户端接收消息回调", serverPoint + "异常【" + ex.Message + "】");
}
}
}/// <summary>
/// 向服务端发送消息
/// </summary>
/// <param name="sendMsg"></param>
public static void Send(Socket socket, byte[] sendmessage)
{
try
{
if (socket != null && socket.Connected)
{
socket.BeginSend(sendmessage, 0, sendmessage.Length, 0, new AsyncCallback(SendCallback), socket);
}
}
catch (Exception ex)
{
MessageBox.Show("异常-客户端发送消息异常" + socket.LocalEndPoint, serverPoint + "【" + ex.Message + "】");
}
}
//发送消息异步回调
private static void SendCallback(IAsyncResult ar)
{
try
{
// 从异步状态中检索socket
Socket client = (Socket)ar.AsyncState;
// 完成将数据发送到服务端
int bytesSent = client.EndSend(ar);
// 表示所有自己均已发送
//sendDone.Set();
}
catch (Exception ex)
{
MessageBox.Show("异常-客户端发送消息回调异常" + client.LocalEndPoint, serverPoint + "【" + ex.Message + "】");
}
}
ClientMess clm;
private void button10_Click(object sender, EventArgs e)//添加客户端
{
if(textBox4.Text.Length>0&& textBox5.Text.Length > 0)
{
bool m = Regex.IsMatch(textBox4.Text, "^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$"); // 匹配正则表达式
if (m == false)
{
MessageBox.Show("IP输入格式不正确!请修改");
return;
}
clm =new ClientMess();
clm.CIp=textBox4.Text;
clm.CPort=textBox5.Text;
comlistDic.Add(comid, clm);
comListstringDic.Add(comid, textBox4.Text + ":" + textBox5.Text);
comboBox2.ValueMember = "key";
comboBox2.DisplayMember = "value";
comboBox2.DataSource = new BindingSource() { DataSource = comListstringDic };
comid++;
}
}private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)//根据不同客户端切换接收区内容
{
if (comboBox2.SelectedItem==null)
{
return;
}
var aa = ((KeyValuePair<int, string>)comboBox2.SelectedItem).Key;//结构体,定义可设置或检索的键/值对。也就是说我们可以通过 它记录一个键/值对这样的值。将选择的值转换以方便获取键值
if (comlistDic[aa].CIfConn)
{
button6.Enabled = false;
button7.Enabled = true;
}
else
{
button6.Enabled = true;
button7.Enabled = false;
}
NowClientClass = comlistDic[aa];
if (NowClientClass.CSocket != null&&NowClientClass.CSocket.Connected)
{
label6.Text = NowClientClass.CSocket.LocalEndPoint.ToString();
}
else
{
label6.Text = "0";
}
textBox7.Text = NowClientClass.CRecvStr;
}private void button2_Click(object sender, EventArgs e)//清空接收区
{
textBox7.Text = string.Empty;
NowClientClass.CRecvStr = "";
}private void button6_Click(object sender, EventArgs e)//连接
{
int index = comboBox2.Text.IndexOf(":");
if(index != -1)
{
ConnectServercer(comboBox2.Text.Substring(0, index), comboBox2.Text.Substring(index+1));
}
}private void button7_Click(object sender, EventArgs e)//断开
{ if(NowClientClass.CIfConn)
{
NowClientClass.CSocket.Shutdown(SocketShutdown.Both);//停止接收发送数据
NowClientClass.CSocket.Close();//关闭连接
}
button6.Enabled = true;
button7.Enabled = false;
NowClientClass.CIfConn = false;
}private void button11_Click(object sender, EventArgs e)//删除
{
if (comboBox2.Text.Length > 0)
{
if (NowClientClass.CIfConn)
{
NowClientClass.CSocket.Shutdown(SocketShutdown.Both);//停止接收发送数据
NowClientClass.CSocket.Close();//关闭连接
}
NowClientClass.CIfConn = false;
NowClientClass.CRecvStr = "";
textBox7.Text = "";
var aa = ((KeyValuePair<int, string>)comboBox2.SelectedItem).Key;
comlistDic.Remove(aa);
comListstringDic.Remove(aa);
if (comListstringDic.Count > 0)
{
comboBox2.DataSource = new BindingSource() { DataSource = comListstringDic };
}
else
{
comboBox2.DataSource= null;
}
label6.Text = "0";
}
}private void button14_Click(object sender, EventArgs e)//发送消息
{
if (comboBox2.Text.Length > 0&&NowClientClass.CIfConn)
{
if (radioButton4.Checked)//十六进制发送消息
{
if ((textBox6.Text.Length % 2) != 0)
textBox6.Text = textBox6.Text.Insert(textBox6.Text.Length - 1, 0.ToString());
byte[] returnBytes = new byte[textBox6.Text.Length / 2];
for (int i = 0; i < returnBytes.Length; i++)
returnBytes[i] = Convert.ToByte(textBox6.Text.Substring(i * 2, 2), 16);
//根据客户端IP端口找到字典集合socket
Send(NowClientClass.CSocket, returnBytes);
}
else
{
Send(NowClientClass.CSocket, Encoding.Default.GetBytes(textBox6.Text));
}
NowClientClass.CRecvStr += (DateTime.Now +"【发送】数据 :" + textBox6.Text + "\r\n");
textBox7.Text = NowClientClass.CRecvStr;
}
}private void textBox5_KeyPress(object sender, KeyPressEventArgs e)
{
//如果输入的不是退格和数字,则屏蔽输入
if (!(e.KeyChar == '\b' || (e.KeyChar >= '0' && e.KeyChar <= '9')))
{
e.Handled = true;
}
}