概述
TCP长连接Demo
网络连接:1、官方解释自行百度
解释(for me):网络连接有客户端和服务端两部分组成,一个服务端接收多条客户端数据并进行回复的操作。客户端收到数据和客户端请求都有对应状态,并且是并发执行,故需要多线程进行监听调用。每一个客户端的请求都是需要经过多条路由后连接到服务端,反之接收数据同理。
tcp长连接形式会导致服务端很容易崩溃,比如100个线程同时运行时,32位系统会直接崩溃,故实际项目中不建议使用,以下只做为客户端和服务端的初步了解。
以下仅对网络连接进行模拟接收数据的基本操作,做一个基本的了解,首先建立服务端:
1、服务端样图如下(namespace下的所有代码
):
public partial class LongServerForm : Form
{
public LongServerForm()
{
InitializeComponent();
}
//声明socket
private Socket server = null;
//用于存储线程个数及相应信息 至少有一个新的空间,不能设置为空,设置为空你是收不到数据的。
private List<Socket> listClientIP = new List<Socket>();
//声明启动的线程
private Thread acceptThread = null;
/// <summary>
/// 启动端口的按钮 实现打开固定端口的服务端
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
try
{
//创建socket对象 InterNetwork:网络连接形式 Stream:流的请求形式 协议类型:tcp
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建特定的IP地址和端口组合
IPEndPoint point = new IPEndPoint(IPAddress.Any, Convert.ToInt32(txtServerPort.Text.Trim()));
server.Bind(point);//绑定端口
server.Listen(10);//监听数为10
labDynamicMes.Text = "启动成功,等待客户端连接中";
//启动线程执行 执行具体方法
acceptThread = new Thread(new ThreadStart(Accpet));
//设置主线程结束时子线程跟着结束
acceptThread.IsBackground = true;
acceptThread.Start();
//关闭按钮,防止通信再次启动
btnStart.Enabled = false;
}
catch (Exception ex)
{
//Thread.Sleep(5000);
}
}
/// <summary>
/// 多线程重点方法
/// </summary>
/// <param name="col"></param>
/// <param name="method"></param>
public void InvokeMethod(Control col, MethodInvoker method)
{
//是否被创建控件 未创建返回空
if (!col.IsHandleCreated) return;
//是否占用(释放) 占用返回空
if (col.IsDisposed) return;
//多线程请求调用(多开一个线程)
if (col.InvokeRequired) col.Invoke(method);
else method();
}
//接收客户端连接
private void Accpet()
{
while (true)
{
try
{
Socket client = server.Accept();//设置客户端的请求允许连接
listClientIP.Add(client);//记录
//允许连接后需要创建接收数据的线程,保证数据可接收,有返回 注意方法的含有object的参数
Thread clientThread = new Thread(new ParameterizedThreadStart(ReviceData));
//设置主线程结束时子线程跟着结束
clientThread.IsBackground = true;
clientThread.Start(client);
//请输入基本的逻辑操作 注意一定使用方法InvokeMethod 本质避免系统窗体卡死等问题 哈哈哈 不信你试试
InvokeMethod(this,delegate
{
labDynamicMes.Text = "有" + listClientIP.Count.ToString() + "个客户端连接!";
lbcIPPort.Items.Clear();
foreach (Socket item in listClientIP)
{
lbcIPPort.Items.Add(item.RemoteEndPoint.ToString());
}
});
Thread.Sleep(2000);//断开后一秒自动获取客户端
}
catch (Exception ex)
{
//Thread.Sleep(5000);
}
}
}
/// <summary>
/// 接收数据
/// </summary>
/// <param name="obj"></param>
private void ReviceData(object obj)
{
while (true)
{
Socket revicesocker = obj as Socket;//客户端对象
if (obj == null)
continue;
try
{
byte[] data = new byte[1024 * 1024 * 10];
//创建数据缓存
int length = revicesocker.Receive(data);
if (length == 0) continue;
//如果取到数据则执行逻辑上的操作 嘿嘿,注意方法InvokeMethed
InvokeMethod(this,delegate{
lbcClientMes.Items.Add(Encoding.Default.GetString(data));
});
}
//
catch (Exception ex)
{
//断开代码
listClientIP.Remove(revicesocker);
//InvokeMethed说 :没错又是我,我是代码核心
InvokeMethod(this, delegate
{
labDynamicMes.Text = "有" + listClientIP.Count.ToString() + "个客户端连接!";
lbcIPPort.Items.Clear();
foreach (Socket k in listClientIP)
{
lbcIPPort.Items.Add(k.RemoteEndPoint.ToString());
}
});
}
}
}
/// <summary>
/// 发送按钮触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnServerSend_Click(object sender, EventArgs e)
{
if (lbcIPPort.SelectedIndex < 0) return;
Socket client = listClientIP[lbcIPPort.SelectedIndex];
if (client == null) return;
if (txtServerSend.Text.Trim() == "")
{
MessageBox.Show("别闹,请在左侧文本框中输入数据后进行发送操作");
}
if (client.Connected == false)
{
MessageBox.Show("客户端已断开无法发送数据!"); return;
}
client.Send(Encoding.Default.GetBytes(txtServerSend.Text.Trim()));
}
}
2、客户端的连接窗体及代码如下(namespace下的所有):
public partial class LongClientForm : Form
{
public LongClientForm()
{
InitializeComponent();
}
private Socket clientsocket = null;
public class StateObject
{
public Socket workSocket = null;
public const int BUFFER_SIZE = 1024 * 1024 * 5;
public byte[] buffer = new byte[BUFFER_SIZE];
public StringBuilder sb = new StringBuilder();
}
/// <summary>
/// 连接按钮事件
/// 连接服务器端
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
clientsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientsocket.ReceiveTimeout = 5000;
clientsocket.ReceiveBufferSize = 1024 * 1024 * 5;
//创建特定IP地址和端口组合
IPEndPoint ippoint = new IPEndPoint(IPAddress.Parse(txtIP.Text.Trim()), Convert.ToInt32(txtPort.Text.Trim()));
//创建好连接
clientsocket.Connect(ippoint);
//防止线程池阻塞,调用BeginReceive执行回调函数 直到EndReceive从socket的缓冲区中读到数据或者socket引发异常。
StateObject stateobject = new StateObject();
stateobject.workSocket = clientsocket;
clientsocket.BeginReceive(stateobject.buffer, 0, StateObject.BUFFER_SIZE, SocketFlags.None, new AsyncCallback(TcpIpClientRead_Callback), stateobject);
btnSend.Enabled = true;
}
catch (Exception ex)
{
InvokeMethod(this, delegate
{
lbcBack.Items.Add("连接服务器失败" + ex.Message);
});
}
}
/// <summary>
/// 回调函数主体
/// </summary>
/// <param name="ar"></param>
private void TcpIpClientRead_Callback(IAsyncResult ar)
{
StateObject stateobject = (StateObject)ar.AsyncState;
Socket s = stateobject.workSocket;
int read = 0;
try
{
read = clientsocket.EndReceive(ar);
string temp = System.Text.Encoding.Default.GetString(stateobject.buffer, 0, read);
InvokeMethod(this, delegate
{
lbcBack.Items.Add("收到数据:" + temp);
});
StateObject so1 = new StateObject();
so1.workSocket = s;
s.BeginReceive(so1.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(TcpIpClientRead_Callback), so1);
}
catch (Exception ex)
{
//InvokeMethod(this, delegate
// {
// lbcBack.Items.Add("接收数据发生未知错误,信息:" + ex.Message);
// });
}
}
/// <summary>
/// 向服务器发送有数据的请求
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
if (clientsocket == null) return;
if (txtSendMes.Text.Trim() == "")
{
MessageBox.Show("你都没写请求谁呢!让我如何做着实很无奈");
}
clientsocket.Send(System.Text.Encoding.Default.GetBytes(txtSendMes.Text.Trim()));
}
/// <summary>
/// 多线程重点方法
/// </summary>
/// <param name="col"></param>
/// <param name="method"></param>
public void InvokeMethod(Control col, MethodInvoker method)
{
//是否被创建控件 未创建返回空
if (!col.IsHandleCreated) return;
//是否占用(释放) 占用返回空
if (col.IsDisposed) return;
//多线程请求调用(多开一个线程)
if (col.InvokeRequired) col.Invoke(method);
else method();
}
}
存在一个小问题:服务端接收客户端发送的文字信息在服务端不展示,虽然加入到队列,但是数据为空,但是服务器端在对固定IP返回文字信息的数据时却可以展示完整的信息,具体原因还未找到,找到后会另行补充。
运行步骤:一个解决方案中建立两个项目,先启动服务端程序,后启动客户端程序,先开启服务端端口后,在客户端对服务端点击按钮连接进行连接,可输入要传入的信息后进行发送,发送的具体内容会展示在服务端的listbox中。相反,服务端返回的数据输入后,指定要返回的IP地址,会将返回的信息展示在客户端的listbox上面。
注意:开发过程中不建议使用,使用后很容易导致服务器崩溃,占用资源极大。下期我会更新常用进程通信形式,简单模拟银行叫号系统服务与客户的连接。
最后
以上就是壮观帽子为你收集整理的C# Socket进程通信- - - -长连接实现方式的理解(Tcp)的全部内容,希望文章能够帮你解决C# Socket进程通信- - - -长连接实现方式的理解(Tcp)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复