简单的异步TCP聊天应用程序[C#]

时间:2010-10-10 09:39:16

标签: c# tcp

好的,我研究了异步TCP网络连接。我试过制作但失败了。我想要做的是确保服务器或客户端随时准备接收聊天,并且能够随时发送聊天。我不希望他们处于备用模式。

e.g。客户端等待接收时服务器发送,因此客户端当时无法发送。 我不想要那个!

在Windows应用程序上做了这个。连接后,系统资源刚刚达到100%= /

服务器代码

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.Net.Sockets;

namespace AsyncServerChat
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private Socket g_server_conn;
        private byte[] g_bmsg;
        private bool check = false;
        private void Form1_Load(object sender, EventArgs e)
        {
            IPEndPoint local_ep = new IPEndPoint(IPAddress.Any, 9050);

            Socket winsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            winsock.Bind(local_ep);

            winsock.Listen(5);

            winsock.BeginAccept(new AsyncCallback(Accept), winsock);
        }

        private void Accept(IAsyncResult iar)
        {
            Socket server_conn =(Socket) iar.AsyncState;

            g_server_conn = server_conn.EndAccept(iar);

            //label1.Text = "Connected. . .";

            while (g_server_conn.Connected && check == false)
            {
                g_bmsg = new byte[1024];
                check = true;
                g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(Recieve), g_server_conn);
            }
        }

        private void Send(IAsyncResult iar)
        {
            Socket server_conn = (Socket)iar.AsyncState;

            server_conn.EndSend(iar);
        }

        private void Recieve(IAsyncResult iar)
        {
            Socket server_conn =(Socket) iar.AsyncState;

            server_conn.EndReceive(iar);

            if (g_bmsg.Length != 0)
            {
                label1.Text = Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length);
                check = false;
            }
        }

        private void sendButton_Click(object sender, EventArgs e)
        {
            string strmsg = textBox1.Text;
            byte[] bmsg= Encoding.ASCII.GetBytes(strmsg);

            g_server_conn.BeginSend(bmsg, 0, bmsg.Length, SocketFlags.None, new AsyncCallback(Send), g_server_conn);
        }
    }
}

客户端

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;

namespace AsyncClientChat
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        Socket g_client_conn;
        byte[] g_bmsg;
        private bool check = false;
        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void connectButton_Click(object sender, EventArgs e)
        {
            IPEndPoint remote_ep = new IPEndPoint(IPAddress.Parse(textBox1.Text), 9050);

            g_client_conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            g_client_conn.BeginConnect(remote_ep, new AsyncCallback(Connect), g_client_conn);
        }

        private void Connect(IAsyncResult iar)
        {
            Socket client_conn =(Socket) iar.AsyncState;

            client_conn.EndConnect(iar);

            while (g_client_conn.Connected)
            {
                g_bmsg = new byte[1024];
                check = true;
                g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(Recieve), g_client_conn);
            }
        }

        private void Send(IAsyncResult iar)
        {
            Socket client_conn = (Socket)iar.AsyncState;

            client_conn.EndSend(iar);
        }

        private void Recieve(IAsyncResult iar)
        {
            Socket client_conn = (Socket)iar.AsyncState;

            client_conn.EndReceive(iar);

            if (g_bmsg.Length != 0)
            {
                label1.Text = Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length);
                check = false;
            }
        }
    }
}

2 个答案:

答案 0 :(得分:1)

嗯,问题是客户端方法while中的Connect循环。 删除它,因为它无限循环地将CPU使用率提高到100%并且没用。

顺便说一句,您的代码中还有其他问题:

  • CrossThread操作例外

例如,在你的Client.Recieve方法中,你可以:  label1.Text = Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length);
实际上,您正在尝试从另一个线程(侦听收到的消息)中设置标签文本,这是不允许的;
做这样的事情:

为标签文本创建一个Setter方法:

private void SetLabelText(string txt)
{
    if (label1.InvokeRequired)
        label1.Invoke(new MethodInvoker(delegate { SetLabelText1(txt); }));
    else
        label1.Text = txt;
}

然后使用setter而不是直接调用label1.Text = ...

SetLabelText(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));

编辑回答OP评论:

有关什么是线程的详细解释,请查看其wikipedia page

无论如何,简单来说,正在运行的进程包含一个或多个线程,这些是可以同时执行的代码的一部分。

从您的TCP示例开始,使用Socket.Receive代替Socket.BeginReceive,您将阻止Socket.Receive()调用的执行(我的意思是包含Receive的代码行之后的代码行在收到某些内容之前,不会达到方法。

这是因为Socket.Receive方法在以下代码的同一个线程上运行,并且在每个线程上,代码按顺序执行(即逐行执行)。

相反,使用Socket.BeginReceive,在场景后面创建一个新线程。这个线程可能在Socket.Receive方法上调用和停止,一旦收到它调用作为参数传递的方法。

这会使Socket.BeginReceive异步,而Socket.Receive是同步的,这就是为什么我知道这是另一个线程(当你听到异步字时,极有可能你正在处理多线程)

因此,当您更改label.Text时,实际上是从另一个线程设置它:Socket.BeginReceive创建的那个。

答案 1 :(得分:1)

我快速查看了这段代码,我将从以下建议开始。

Accept回调中移除循环,只需启动BeginReceive即可。然后在Receive方法中,您可以启动下一个BeginReceive。这将适用于客户端和服务器代码,当然除了客户端代码,您将从Connect回调方法中删除循环。

然后您还应该注意从回调方法更新UI控件,因为回调在非UI线程上运行,这可能会导致一系列问题。您应该查看使用Control.InvokeControl.BeginInvoke将请求编组回UI线程,然后可以更新控件。