C# TCP/IP 异步Socket 断线重连的问题

tom123gzk 2017-04-10 03:41:46
最近一直在研究Socket ,完成了同步之后在异步的过程中遇到了一个问题,2天了一直没有解决。

问题描述:
要求灵活性比较高例如 1.客户端服务端不分顺序,任意顺序打开要求能连接上(打开程序就连接,不需要按钮)。
2.在连接过程中,如果关闭客户端,重启客户端(服务端不做任何操作),自动连接上。
3.在连接过程中,如果关闭服务器,重启服务器(客户端不做任何操作),自动连接上。

其中,第1个第2个都已经解决,针对第3个问题,我的客户端检测到断开后,一直循环重连直到再次连接上,但是实际情况上,客户端的循环要多执行一次,每次都会导致连接上2个,同一个IP不同的端口(其实只有一个客户端)如下图:


部分代码如下:

private void Connect()
{
Thread tConnect = new Thread(new ThreadStart(ConnectServer));
tConnect.IsBackground = true;
tConnect.Start();
}

private void ConnectServer()
{
MsgBuffer = new Byte[65535];
int timeStart = System.Environment.TickCount;
IPAddress ipAddress = IPAddress.Parse("192.168.6.93");
IPEndPoint remoteEP = new IPEndPoint(ipAddress, 1022);
bool bChange = false;
while (true)
{
if (!bConnection)
{
try
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
}
catch (Exception)
{
if (!bChange)
{
this.Invoke((EventHandler)(delegate
{
txtRcv.Text = "连接服务器失败,请确认服务器是否正常工作!";
pictureBox1.Image = Image.FromFile(".\\pic\\bullet_red.png");
}));
bChange = true;
}
continue;
}
}
else
{
break;
}
t = new Thread(o => Thread.Sleep(500));
t.Start(this);
while (t.IsAlive)
{
Application.DoEvents();
}
}
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
client.EndConnect(ar);
connectDone.Set();
MsgBuffer = new Byte[65535];
client.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), client);
this.Invoke((EventHandler)(delegate
{
bConnection = true;
bFirst = true;
txtRcv.Text = "已连接上服务器";
pictureBox1.Image = Image.FromFile(".\\pic\\bullet_green.png");
}));
receiveDone.WaitOne();
}
catch (Exception)
{
if (!bchange)
{
this.Invoke((EventHandler)(delegate
{
txtRcv.Text = "连接服务器失败,请确认服务器是否正常工作!";
pictureBox1.Image = Image.FromFile(".\\pic\\bullet_red.png");
}));
bchange = true;
}
if (!bFirst) ConnectServer();
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
this.Invoke((EventHandler)(delegate
{
string strTemp = Encoding.ASCII.GetString(MsgBuffer, 0, bytesRead);
txtRcv.Text = strTemp;
ShowData(strTemp);
}));
client.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), client);
}
else
{
receiveDone.Set();
}
}
catch (Exception)
{
this.Invoke((EventHandler)(delegate
{
bConnection = false;
bFirst = true;
txtRcv.Text = "连接服务器失败,请确认服务器是否正常工作!";
pictureBox1.Image = Image.FromFile(".\\pic\\bullet_red.png");
}));
//client.Close();
//client = null;
ConnectServer();
}
}
...全文
3468 19 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
引用 17 楼 sp1234 的回复:
关于什么是异步,我拿这里的代码举个例子,比如说(这里没有检测异常,只是例子)
void Connect(Socket client, Action callback)
{
    client.BeginConnect("...", 1234, ar =>
        {
            client.EndConnect(ar);
            if (callback != null)
                callback();
        }, null);
}
这种代码,总是有 callback 机制,这就叫做异步。 并不是说弄什么线程才叫异步。往往你要顺着callback 委托的思路设计,在一个事件发生时则回调 callback,这样的程序才简练。 切不可乱用线程,更不可在子线程中写一个死循环代码。
大神说的对呦
tom123gzk 2017-04-10
  • 打赏
  • 举报
回复
引用 17 楼 sp1234 的回复:
关于什么是异步,我拿这里的代码举个例子,比如说(这里没有检测异常,只是例子)
void Connect(Socket client, Action callback)
{
client.BeginConnect("...", 1234, ar =>
{
client.EndConnect(ar);
if (callback != null)
callback();
}, null);
}

这种代码,总是有 callback 机制,这就叫做异步。

并不是说弄什么线程才叫异步。往往你要顺着callback 委托的思路设计,在一个事件发生时则回调 callback,这样的程序才简练。

切不可乱用线程,更不可在子线程中写一个死循环代码。



非常感谢大神指导,受益匪浅。
  • 打赏
  • 举报
回复 5
关于什么是异步,我拿这里的代码举个例子,比如说(这里没有检测异常,只是例子)
void Connect(Socket client, Action callback)
{
    client.BeginConnect("...", 1234, ar =>
        {
            client.EndConnect(ar);
            if (callback != null)
                callback();
        }, null);
}
这种代码,总是有 callback 机制,这就叫做异步。 并不是说弄什么线程才叫异步。往往你要顺着callback 委托的思路设计,在一个事件发生时则回调 callback,这样的程序才简练。 切不可乱用线程,更不可在子线程中写一个死循环代码。
  • 打赏
  • 举报
回复 4
把所有的 WaitOne删除掉,把 bConnection 逻辑废除掉,重新设计异步通讯程序,没有数据通讯是决没有任何线程在那里阻塞着!这是异步通讯程序。
  • 打赏
  • 举报
回复 4
你的代码,拿着异步的语法,在做同步阻塞循环的事情,完全是“假异步”的。真正的异步程序,不会写 connectDone.WaitOne() 和 receiveDone.WaitOne() 这种代码,当然也就不会有 while(true) 这种代码。 关于“断线重连”,是在有数据要发送的时候,假设发送失败,那么判断 socket.Connected,如果返回false则重连一次,重连之后立刻重新发送数据。并不需要什么 bConnection 诡异标志。 我给你打个比方,比如说让你去买手纸,意思是你去商店时要记着顺便买手纸,而不是说让你什么都不干了、上班也在单位买手纸、吃饭也在食堂买手纸、喝水也在水杯里找手纸........你弄了个 while 循环这是不对的。你需要在发送数据失败时知道判断一下,例如
void Connect(Socket client, Action callback)
{
    //.........
}

void Send(Socket client, byte[] message)
{
    try
    {
        client.Send(message);
    }
    catch
    {
        if (!client.Connected)
            Connect(client, () =>
                {
                    client.Send(message);
                });
    }
}
这就是说,在send 的时候知道先连接然后send,这也就重连了。
Poopaye 2017-04-10
  • 打赏
  • 举报
回复
而且明显这里用BeginConnect就是画蛇添足
Poopaye 2017-04-10
  • 打赏
  • 举报
回复
引用 12 楼 tom123gzk 的回复:
[quote=引用 10 楼 shingoscar 的回复:] [quote=引用 9 楼 shingoscar 的回复:] 用socket.Connect(……)
只有失败了再Connect,这样就不会连2次了[/quote] 需要用异步连接的方式,是失败了再连接啊,全部都是放在异常下执行的。[/quote] 哪里有失败了再连接这样的逻辑 不要告诉我是这句if (!bConnection)? 异步怎么能用这种判定方式?
tom123gzk 2017-04-10
  • 打赏
  • 举报
回复
引用 10 楼 shingoscar 的回复:
[quote=引用 9 楼 shingoscar 的回复:] 用socket.Connect(……)
只有失败了再Connect,这样就不会连2次了[/quote] 需要用异步连接的方式,是失败了再连接啊,全部都是放在异常下执行的。
xdashewan 2017-04-10
  • 打赏
  • 举报
回复
引用 4 楼 tom123gzk 的回复:
代码中的 bChange和bchange 是为了不让程序多次执行的信号,bConnection标志位的位置有问题?刚刚看了下,应该没什么问题啊,还请大神指教
没运行过,但你释放信号量的时候,bConnection还未能置true,再加上在EndConnect抛出异常时,你的前一次WaitOne就不能解锁,这些都是隐患
Poopaye 2017-04-10
  • 打赏
  • 举报
回复
引用 9 楼 shingoscar 的回复:
用socket.Connect(……)
只有失败了再Connect,这样就不会连2次了
Poopaye 2017-04-10
  • 打赏
  • 举报
回复
用socket.Connect(……)
tom123gzk 2017-04-10
  • 打赏
  • 举报
回复
引用 6 楼 crystal_lz 的回复:
好抽象的逻辑 t = new Thread(o => Thread.Sleep(500)); t.Start(this); while (t.IsAlive) { Application.DoEvents(); } 我不太明白 这代码是根据什么样子的思路写出来的 而且在非ui线程中 DoEvents有什么意义 而且不论链接成功与否 你的tConnect线程都一直在不停的循环着 这不是摆明没事找事干吗 即使你想意外断开后从连 那也是应该断开后产生了异常后再去尝试重连而不是不停的循环判断 而在你的CallBack中只有链接成功 你才对你的connetcDon进行set 那么也就是说 上面你ConnectServer里面的WaitToOnce还会一直等下去 链接失败情况 你却又去调用ConnectServer 那么如果一直都死链接失败情况下 你的WaitToOnce一直在那里累计 假设累计了10次了 那么你就有10个while true的循环线程了 好666的样子 简直不敢想象。
while true的循环线程 下面有个条件是bConnection 一旦连接上了,会跳出循环,线程也就结束掉了啊,并不会存在多个while true的循环啊
tom123gzk 2017-04-10
  • 打赏
  • 举报
回复
引用 5 楼 diaodiaop 的回复:
不好意思 看错了..我以为你这是服务端的代码..
引用
1.客户端服务端不分顺序,任意顺序打开要求能连接上(打开程序就连接,不需要按钮)。 2.在连接过程中,如果关闭客户端,重启客户端(服务端不做任何操作),自动连接上。 3.在连接过程中,如果关闭服务器,重启服务器(客户端不做任何操作),自动连接上。
你问题问到3 我觉得你的思路有问题了..你服务器关闭了 你客户端怎么可能重启服务端? 那个不是服务器的事吗? 一般来说 服务端的功能 是有问题 重启. 客户端的功能 是断线重连 不是这样吗
问题3的意思是 假如说 现在是正常的连接状态(服务器和客户端已经连接),这个时候有人突然不小心把服务器关了,那么客户端需要自动不断的重新连接直到连接上服务器,而服务端需要做的仅仅是再打开而已,不需要再操作客户端了。
crystal_lz 2017-04-10
  • 打赏
  • 举报
回复
好抽象的逻辑 t = new Thread(o => Thread.Sleep(500)); t.Start(this); while (t.IsAlive) { Application.DoEvents(); } 我不太明白 这代码是根据什么样子的思路写出来的 而且在非ui线程中 DoEvents有什么意义 而且不论链接成功与否 你的tConnect线程都一直在不停的循环着 这不是摆明没事找事干吗 即使你想意外断开后从连 那也是应该断开后产生了异常后再去尝试重连而不是不停的循环判断 而在你的CallBack中只有链接成功 你才对你的connetcDon进行set 那么也就是说 上面你ConnectServer里面的WaitToOnce还会一直等下去 链接失败情况 你却又去调用ConnectServer 那么如果一直都死链接失败情况下 你的WaitToOnce一直在那里累计 假设累计了10次了 那么你就有10个while true的循环线程了 好666的样子 简直不敢想象。
by_封爱 版主 2017-04-10
  • 打赏
  • 举报
回复
不好意思 看错了..我以为你这是服务端的代码..
引用
1.客户端服务端不分顺序,任意顺序打开要求能连接上(打开程序就连接,不需要按钮)。 2.在连接过程中,如果关闭客户端,重启客户端(服务端不做任何操作),自动连接上。 3.在连接过程中,如果关闭服务器,重启服务器(客户端不做任何操作),自动连接上。
你问题问到3 我觉得你的思路有问题了..你服务器关闭了 你客户端怎么可能重启服务端? 那个不是服务器的事吗? 一般来说 服务端的功能 是有问题 重启. 客户端的功能 是断线重连 不是这样吗
tom123gzk 2017-04-10
  • 打赏
  • 举报
回复
引用 2 楼 xdashewan 的回复:
信号量和bConnection标志位代码位置自己再考虑下
代码中的 bChange和bchange 是为了不让程序多次执行的信号,bConnection标志位的位置有问题?刚刚看了下,应该没什么问题啊,还请大神指教
tom123gzk 2017-04-10
  • 打赏
  • 举报
回复
引用 1 楼 diaodiaop 的回复:
有第三方的 很简单 别自己判断了..
没了解过第三方的,哪边可以下载或者看到
xdashewan 2017-04-10
  • 打赏
  • 举报
回复
信号量和bConnection标志位代码位置自己再考虑下
by_封爱 版主 2017-04-10
  • 打赏
  • 举报
回复
有第三方的 很简单 别自己判断了..

111,098

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧