Socket通信
本帖最后由 never_four 于 2020-2-28 23:08 编辑C# ===> WinForm ====> Socket 通信
一.服务端UI
https://attach.52pojie.cn//forum/202002/28/224843vkxh9nwxnnsyhsrn.png?l
二.客户端UI
https://attach.52pojie.cn//forum/202002/28/225004mrkkd825lynr8j1z.png?l
三.Socket通信在Vs中的整体结构图
https://attach.52pojie.cn//forum/202002/28/225221igs96yg3fb17b9k7.png?l
四.服务端代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _001_Server
{
public partial class FrmServer : Form
{
public FrmServer()
{
InitializeComponent();
}
private void FrmServer_Load(object sender, EventArgs e)
{
txtIp.Text = GetLocalIp.IpAddress();
txtPort.Text = "5000";
//关闭对文本框的非法线程操作检查
Control.CheckForIllegalCrossThreadCalls = false;
}
private void btnStartServer_Click(object sender, EventArgs e)
{
try
{
//1.创建监听的Socket
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2.获取当前Ip地址
IPAddress ip = IPAddress.Parse(txtIp.Text);
//3.组成IP地址和Port号
IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPort.Text));
//4.服务器开始对当前应用程序进行监听 (负责监听的Socket绑定IP地址和端口号)
listenSocket.Bind(point);
//5.设置监听队列
listenSocket.Listen(10);//监听队列挂起的最大长度
//6.显示监听成功的信息
ShowReceiveMsg("监听成功,正在等待客户端的连接......");
//7.接收客户端发送过来的信息
//定义线程
Thread th = new Thread(ReConnClientMsg);
//将线程设置为后台线程 (防止影响UI线程)
th.IsBackground = true;
//传入参数 并启动线程
th.Start(listenSocket);
}
catch { }
}
//定义键值对集合 存放连接服务器的客户端的 ip地址 和 Socket 信息
Dictionary<string, Socket> dicSocketList = new Dictionary<string, Socket>();
private void ReConnClientMsg(object socketInfo)
{
try
{
//循环 不停的刷新来自客户端的信息
while (true)
{
//类型转换
Socket listenSocket = socketInfo as Socket;
//1.新建负责和客户端建立连接的 Socket
Socket reConnClientSocket = listenSocket.Accept();
//给键值对集合添加 连接服务器的客户端的 ip 地址 和Socket 信息
dicSocketList.Add(reConnClientSocket.RemoteEndPoint.ToString(), reConnClientSocket);
//给下拉选框中添加 连接服务器客户端的 ip 地址 和端口号
cmbAddressList.Items.Add(reConnClientSocket.RemoteEndPoint.ToString());
//2.显示成功连接客户端的信息
ShowReceiveMsg(reConnClientSocket.RemoteEndPoint.ToString() + ": 连接成功!!!");
//3.连接成功后开始接收来自客户端的信息
Thread th = new Thread(ReClientMsg); //这里的ReclientMsg 是将ReClientMsg()方法,当作 参数使用
//将线程设置为后台线程
th.IsBackground = true;
//启动线程
th.Start(reConnClientSocket);
}
}
catch { }
}
private void ReClientMsg(object sockInfo) //使用线程调用方法是 参数必须是Object 类型
{
//类型转换
Socket ReClientMsgInfo = sockInfo as Socket;
try
{
while (true)
{
//1.新建缓冲区
byte[] buffer = new byte;
//2.将来自客户端的数据放入缓冲区中
int byteNums = ReClientMsgInfo.Receive(buffer);
//如果接收数据的字节大小为0 则终止在聊天区域显示
if (byteNums == 0) return;
//3.定义变量,将缓冲区中的数据转换成字符串
string msg = Encoding.UTF8.GetString(buffer, 0, byteNums);
//4.显示接收到的信息
ShowReceiveMsg(ReClientMsgInfo.RemoteEndPoint.ToString() + ": " + msg);
}
}
catch { }
}
private void btnSendMsg_Click(object sender, EventArgs e)
{
string ip = string.Empty;
try
{
//拿到下拉选框中的IP地址
ip = cmbAddressList.SelectedItem.ToString();
}
catch (Exception)
{
MessageBox.Show("请在地址选择要发送信息的对象", "提示:", MessageBoxButtons.OK, MessageBoxIcon.Error);
#region 三个参数的含义
//第一个参数是:提示内容
//第二个参数是:提示框标题
//的三个参数是:突时框中的错误图标
#endregion
return;
}
if (string.IsNullOrEmpty(txtSendMsg.Text.Trim().ToString()))
{
MessageBox.Show("消息内容不能为空", "提示:", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//将待发送的信息转换为字节数组
byte[] buffer = Encoding.UTF8.GetBytes(txtSendMsg.Text.Trim());
//给客户端发送消息
dicSocketList.Send(buffer);
//在聊天区域显示服务端发送的信息
ShowSendMsg(txtSendMsg.Text.Trim());
//给客户端发送一次信息清空一次文本框中的信息
txtSendMsg.Clear();
}
#region 显示信息
public void ShowReceiveMsg(string str)
{
txtChatList.AppendText(DateTime.Now.ToString() + "\r\n" + str + "\r\n");
}
public void ShowSendMsg(string str)
{
txtChatList.AppendText(DateTime.Now.ToString() + "\r\n" + "我: " + str + "\r\n");
}
#endregion
}
}
五.服务端获取本机IP地址代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace _001_Server
{
publicclass GetLocalIp
{
public static string IpAddress()
{
try
{
//1.得到主机名
string hostName = Dns.GetHostName();
//2.新建IpHostEntry容器,存放Ip地址
IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
//3.从hostEntry容器中得到IPV4地址
for (int i = 0; i < hostEntry.AddressList.Length; i++)
{
//如果存放IP地址中的某一个成员是IPV4地址则返回这个IP地址
if (hostEntry.AddressList.AddressFamily==AddressFamily.InterNetwork)
{
return hostEntry.AddressList.ToString();
}
}
}
catch (Exception ex)
{
return ex.Message;
}
return "";
}
}
}
六.客户端代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _002_Client
{
public partial class FrmClient : Form
{
public FrmClient()
{
InitializeComponent();
}
Socket socket;
private void btnConnServer_Click(object sender, EventArgs e)
{
try
{
//1.新建负责与服务器端通信的Socket
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
#region 三个参数
//第一个参数是:Ip地址的类型IPV4
//第二个参数是:Socket 的类型 Socket 类型有两种===> 1. 流式(用于Tcp服务), 2.数据报形式(用于Udp服务)
//第三个参数式:服务类型
#endregion
//2.获取ip地址
IPAddress ip = IPAddress.Parse(txtIp.Text);
//3.将Ip地址和Port号 组合在一起
IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPort.Text));
//4.将ip地址和port号 放入Socket中 并连接服务器端
socket.Connect(point);
//5.提示连接服务器端成功!
ShowReServerMsg("连接服务器成功!!!", socket);
//6.接收来自服务器端的信息
//创建线程 ===> 客户端会不停的刷新 是否有来自服务器端的消息这样会导致主窗体的UI线程执行耗时操作
Thread th = new Thread(ReServerMsg);
//设置线程为后台线程
th.IsBackground = true;
//启动线程
th.Start();
}
catch { }
}
/// <summary>
/// 接收来自服务端的信息
/// </summary>
private void ReServerMsg()
{
try
{
while (true)
{
//1.新建缓冲区
byte[] buffer = new byte;
//2.将来自服务器端的字节类型数组信息放入缓冲区
int byteNums = socket.Receive(buffer);
if (byteNums == 0) return;
//3.将字节数组转换为字符串类型
string msg = Encoding.UTF8.GetString(buffer, 0, byteNums);
#region 三个参数的意思
//第一个参数是:缓冲区中的数据
//第二个参数是:从字节类型数组缓冲区的0位开始转换数据
//第三个参数是:服务器传入的字节类型数组的实际大小
#endregion
//4.在客户端聊天区域显示服务器端发送来的信息
ShowReServerMsg(msg, socket);
}
}
catch { }
}
private void FrmClient_Load(object sender, EventArgs e)
{
//关闭对文本框的非法线程操作检查
Control.CheckForIllegalCrossThreadCalls = false;
}
private void btnSendMsg_Click(object sender, EventArgs e)
{
//1.如果消息编辑区域的信息为空则提示,并终止给服务器发送信息
if (string.IsNullOrEmpty(txtSendMsg.Text.Trim().ToString()))
{
MessageBox.Show("消息内容不能为空!", "提示:", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//2.将发送给服务器端的信息转换为字节数组类型
byte[] buffer = Encoding.UTF8.GetBytes(txtSendMsg.Text.Trim());
//3.给服务器端发送消息
socket.Send(buffer);
//4.在客户端消息列表中显示给服务器发送的信息
ShowSendServerMsg(txtSendMsg.Text.ToString());
//5.给服务端发送一次信息,清除一次消息编辑区域的信息
txtSendMsg.Clear();
}
#region 显示信息
/// <summary>
/// 显示接收来自服务器端的信息
/// </summary>
/// <param name="str">提示信息</param>
/// <param name="socketInfo">服务器端的Socket的信息</param>
private void ShowReServerMsg(string str, Socket socketInfo)
{
//SockInfo.RemoteEndPoint.ToString(); 获得服务器端的Ip地址和端口号
//DateTime.Now.ToString();获取当前时间日期
txtChatList.AppendText(DateTime.Now.ToString() + "\r\n" + socketInfo.RemoteEndPoint.ToString() + ": " + str + "\r\n");
}
/// <summary>
/// 在客户端信息列表中显示发送给服务器端的信息
/// </summary>
/// <param name="str"></param>
private void ShowSendServerMsg(string str)
{
txtChatList.AppendText(DateTime.Now.ToString() + "\r\n" + "我: " + str + "\r\n");
}
#endregion
}
}
七.代码源文件Gitee地址
https://gitee.com/Never_Fours/Socket.git
注释特别详细,适合学Socket通信的新手哦!!!:Dweeqw:Dweeqw:Dweeqw 不是内行人看不懂这些是什么内容! 感谢楼主分享! 学习了,一般都在用封装好的 lyswj 发表于 2020-3-1 10:16
学习了,一般都在用封装好的
万丈高楼,平地起嘛 代码复制下来学习了 地址内容404了 iliulin 发表于 2021-1-28 17:20
地址内容404了
https://gitee.com/Code_Ha/Socket.git
重新补一个地址
页:
[1]