C# Tcp 异步通讯----客户端关闭后服务器的窗体也关闭了

ZAIJIANLUOYE110 2013-03-24 01:25:35
问题1:在我的客户端关闭后,服务器怎么也关闭了;
问题2:如果服务器要连接多个客户端,我想在AcceptCallback里继续调用Socket.BeginAccpet为什么不可以呢?如何才能连接多个客户端?

代码如下:
【服务器端】

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.Threading;
using System.Windows.Forms;

namespace AsyncTcpServer {

public partial class Form1 : Form {
public const int BUFFSIZE = 1024;
public Socket m_server;
public byte[] m_buffer = new byte[BUFFSIZE];

// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);

public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
//从程序设计上来说,只有创建界面的主线程才能访问界面上的控件
//因为我们在OnReceive中访问了控件,所以这里设置成false
CheckForIllegalCrossThreadCalls = false;
// Create a TCP/IP socket.
m_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 9998);

try {
m_server.Bind(localEndPoint);
m_server.Listen(100);

//while (true)
//{
// allDone.Reset();
m_server.BeginAccept(
new AsyncCallback(AcceptCallback),
m_server);
// allDone.WaitOne();
//}
} catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
}

public void AcceptCallback(IAsyncResult ar) {
try {
//allDone.Set();
// Get the socket that handles the client request.
//Socket listener = (Socket)ar.AsyncState;
Socket handler = m_server.EndAccept(ar); //返回的是什么?服务器的socket????

m_server = handler;

handler.BeginReceive(m_buffer, 0, m_buffer.Length, 0,
new AsyncCallback(ReceiveCallback), handler);
} catch (Exception ex) {
m_server.BeginAccept(new AsyncCallback(AcceptCallback), m_server);
}

}

public void ReceiveCallback(IAsyncResult ar) {

String content = String.Empty;
// Read data from the client socket.
int bytesRead = m_server.EndReceive(ar);
string strData = Encoding.ASCII.GetString(m_buffer, 0, bytesRead);
try {
if (bytesRead > 0) {
if (strData.Equals("closeClient")) {
strData = "client has gotten out...";
}
lstContent.Items.Add(strData);
}
m_server.BeginReceive(m_buffer, 0, m_buffer.Length, 0,
new AsyncCallback(ReceiveCallback), m_server);
} catch (Exception ex) {
//MessageBox.Show(ex.ToString());
m_server.BeginAccept(new AsyncCallback(AcceptCallback), m_server);
}
}

private void Send(String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);

// Begin sending the data to the remote device.
m_server.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), m_server);
}

private void SendCallback(IAsyncResult ar) {
try {
// Complete sending the data to the remote device.
int bytesSent = m_server.EndSend(ar);

//handler.Shutdown(SocketShutdown.Both);
//handler.Close();

} catch (Exception e) {
MessageBox.Show(e.ToString());
}
}
}
}



【客户端】
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Threading;
using System.Net.Sockets;

namespace AsyncTcpClient
{
public partial class Form1 : Form
{
public Socket m_client;
public EndPoint m_epServer;
public byte[] m_buffer;
public int port = 9998;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
m_epServer = new IPEndPoint(ipAddress, port);
m_client = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
m_client.BeginConnect(m_epServer,
new AsyncCallback(ConnectCallback), m_client);
}
catch (Exception ex)
{

}
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
// Complete the connection.
m_client.EndConnect(ar);

lstContent.Items.Add("Socket connected to "+
m_client.RemoteEndPoint.ToString());
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}

private void Receive()
{
try
{
// Begin receiving the data from the remote device.
m_client.BeginReceive(m_buffer, 0, m_buffer.Length, 0,
new AsyncCallback(ReceiveCallback), m_client);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

private void ReceiveCallback(IAsyncResult ar)
{
try
{
// Read data from the remote device.
int bytesRead = m_client.EndReceive(ar);

if (bytesRead > 0)
{
lstContent.Items.Add(Encoding.ASCII.GetString(m_buffer, 0, bytesRead));
}
m_client.BeginReceive(m_buffer, 0, m_buffer.Length, 0,
new AsyncCallback(ReceiveCallback), m_client);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}

private void Send(String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);

// Begin sending the data to the remote device.
m_client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), m_client);
}

private void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;

// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
lstContent.Items.Add(String.Format("Sent {0} bytes to server.", bytesSent));
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

private void btnSend_Click(object sender, EventArgs e)
{
if (txtMessage.Text != string.Empty)
{
Send(txtMessage.Text);
}
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Send("closeClient");//发送关闭客户端指令
}
}
}
...全文
544 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
ZAIJIANLUOYE110 2013-03-29
  • 打赏
  • 举报
回复
还没搞懂……谁来帮我解答啊?????????????????????
ZAIJIANLUOYE110 2013-03-27
  • 打赏
  • 举报
回复
引用 14 楼 qldsrx 的回复:
//Socket listener = (Socket)ar.AsyncState; Socket handler = m_server.EndAccept(ar); 看下这两句,第一句被你屏蔽了,关键点也就是它,这两个Socket是不同的Socket,第一个listener通过ar获取的,那个才是你服务端用来监听的Socket,而第二个只是同客户端建立连接的Socket,不是监听用的Socket
我把AcceptCallBack改成了如下:

public void AcceptCallback(IAsyncResult ar) {
            try {
                // Get the socket that handles the client request.
                Socket listener = (Socket)ar.AsyncState;
                Socket handler = listener.EndAccept(ar); 

                handler.BeginReceive(m_buffer, 0, m_buffer.Length, 0,
                    new AsyncCallback(ReceiveCallback), handler);

                listener.BeginAccept(new AsyncCallback(AcceptCallback), handler);
            } catch (Exception ex) {
                MessageBox.Show(ex.ToString());
            }

        }
然后就晕了,listener和handler与m_server这三个Socket不一样吗? 在AcceptCallBack中调用BeginAccept的是m_server还是listener啊? 改成如上的代码后,没异常了,但是当客户端发送消息时,服务器就自动关闭了
ZAIJIANLUOYE110 2013-03-27
  • 打赏
  • 举报
回复
引用 14 楼 qldsrx 的回复:
//Socket listener = (Socket)ar.AsyncState; Socket handler = m_server.EndAccept(ar); 看下这两句,第一句被你屏蔽了,关键点也就是它,这两个Socket是不同的Socket,第一个listener通过ar获取的,那个才是你服务端用来监听的Socket,而第二个只是同客户端建立连接的Socket,不是监听用的Socket
再次感谢您的耐心回答…… //Socket listener = (Socket)ar.AsyncState;把这句恢复后还是和屏蔽时一样的错。 我在调用BeginAccept的时候是这样的
m_server.BeginAccept(new AsyncCallback(AcceptCallback),
                    m_server);
所以Socket listener = (Socket)ar.AsyncState;这句注释和不注释是一样的吧
qldsrx 2013-03-27
  • 打赏
  • 举报
回复
//Socket listener = (Socket)ar.AsyncState; Socket handler = m_server.EndAccept(ar); 看下这两句,第一句被你屏蔽了,关键点也就是它,这两个Socket是不同的Socket,第一个listener通过ar获取的,那个才是你服务端用来监听的Socket,而第二个只是同客户端建立连接的Socket,不是监听用的Socket
qldsrx 2013-03-26
  • 打赏
  • 举报
回复
自动关闭就是发生了错误,但是没有被检测到,比如你这里设置了“CheckForIllegalCrossThreadCalls = false;”,不让程序自动检查跨线程操作,但是这种操作很危险的,很容易出现错误导致系统崩溃。你可以先屏蔽那个频繁的“lstContent.Items.Add”操作,通过变量存储收发内容,最后一次性更新到界面上去。
ZAIJIANLUOYE110 2013-03-26
  • 打赏
  • 举报
回复
引用 12 楼 qldsrx 的回复:
自动关闭就是发生了错误,但是没有被检测到,比如你这里设置了“CheckForIllegalCrossThreadCalls = false;”,不让程序自动检查跨线程操作,但是这种操作很危险的,很容易出现错误导致系统崩溃。你可以先屏蔽那个频繁的“lstContent.Items.Add”操作,通过变量存储收发内容,最后一次性更新到界面上去。
谢谢您的回答,我用Invoke代替了“CheckForIllegalCrossThreadCalls = false;”,这样在AcceptCallBack中调用Socket.BeginAccept后,启动客户端服务器不会自动关闭了。但是在AcceptCallBack中调用BeginAccept抛出必须先调用Socket.Listen的异常,我把Listen加上之后又抛出“在一个已经连接的套接字上做了一个连接请求”,这是怎么回事儿啊?
ZAIJIANLUOYE110 2013-03-25
  • 打赏
  • 举报
回复
引用 10 楼 qldsrx 的回复:
其实不用allDone也可以做到,而且更好,微软这里使用了异步方式编程,但又使用allDone来阻塞,说白了只是为了让你学习这两者东西,而实际编程这样做就是画蛇添足,如果你用了异步方式,就不该阻塞,否则直接同步方法调用Accept方法不就好啦。 正确的做法是在BeginAccept的回调函数AcceptCallback的最后再次执行BeginAccept,这样就可以做到处理完一个连接请求继续处理……
楼上说的对,但是如果在AcceptCallBack中调用BeginAccept,当客户端刚启动时,服务器就自动关闭了……这是为什么啊?
qldsrx 2013-03-25
  • 打赏
  • 举报
回复
其实不用allDone也可以做到,而且更好,微软这里使用了异步方式编程,但又使用allDone来阻塞,说白了只是为了让你学习这两者东西,而实际编程这样做就是画蛇添足,如果你用了异步方式,就不该阻塞,否则直接同步方法调用Accept方法不就好啦。 正确的做法是在BeginAccept的回调函数AcceptCallback的最后再次执行BeginAccept,这样就可以做到处理完一个连接请求继续处理下一个。而且由于最初的BeginAccept不是循环内执行,只执行了一次就离开了,所以不会阻塞UI线程。
qldsrx 2013-03-25
  • 打赏
  • 举报
回复
引用 5 楼 ZAIJIANLUOYE110 的回复:
这里的注释是我自己加的,代码是我把MSDN里的console代码该成现在的Window代码了……如果把注释恢复的话,Server的主窗体将不会显示出来,我感觉是allDone把主线程阻塞了。我尝试了把上面的While放在了新的线程里,但那样还是不对。为什么啊?
allDone就是阻塞线程用的,否则那个while就会无限打开新的监听,这是不合理的,因此需要等待上个监听被连接后,才开始新的监听。因此while必须放在其它线程里执行,而不能直接在窗口控件所在的线程里执行。
wwwww112233 2013-03-24
  • 打赏
  • 举报
回复
你在服务端一步一步debug,看看他是在哪里关闭,为什么会关闭。
ZAIJIANLUOYE110 2013-03-24
  • 打赏
  • 举报
回复
引用 1 楼 sp1234 的回复:
都是127.0.0.1,你的程序从来没有拿两台机器测试过吧?! 先退一步,找个靠谱的例子吧。不要从这个学编程。
没有呢,先在自己机子上试好了再用两台机子,可是出问题了,不知道怎么解决
ZAIJIANLUOYE110 2013-03-24
  • 打赏
  • 举报
回复
引用 2 楼 wwwww112233 的回复:
是异常关闭还是正常关闭
client的窗体正常关闭的时候,服务器的窗体就自动关闭了
ZAIJIANLUOYE110 2013-03-24
  • 打赏
  • 举报
回复
引用 3 楼 qldsrx 的回复:
一看就知道是抄来的代码,而且自己完全不动C#。

//while (true) //{ 
//    allDone.Reset();       
      m_server.BeginAccept(new AsyncCallback(AcceptCallback), m_server); 
//    allDone.WaitOne(); 
//} 
谢谢您的回复,这里的注释是我自己加的,代码是我把MSDN里的console代码该成现在的Window代码了……如果把注释恢复的话,Server的主窗体将不会显示出来,我感觉是allDone把主线程阻塞了。我尝试了把上面的While放在了新的线程里,但那样还是不对。为什么啊?
Kation 2013-03-24
  • 打赏
  • 举报
回复
80%是服务器出现异常 你用Ctrl+F5运行程序,出异常cmd窗口还在,异常会显示在上面。
qldsrx 2013-03-24
  • 打赏
  • 举报
回复
一看就知道是抄来的代码,而且自己完全不动C#。
//while (true)
//{
//    allDone.Reset();
      m_server.BeginAccept(
      new AsyncCallback(AcceptCallback),
      m_server);
//    allDone.WaitOne();
//}
把这里面的注释去掉就可以连接多个客户端了,AcceptCallback方法里面的注释也要去掉,catch部分添加注释,出现异常输出即可,不要自动重新等待连接。
wwwww112233 2013-03-24
  • 打赏
  • 举报
回复
是异常关闭还是正常关闭
  • 打赏
  • 举报
回复
都是127.0.0.1,你的程序从来没有拿两台机器测试过吧?! 先退一步,找个靠谱的例子吧。不要从这个学编程。

111,131

社区成员

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

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

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