Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
简单异步TCP聊天应用程序[C#]_C#_Tcp - Fatal编程技术网

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

简单异步TCP聊天应用程序[C#],c#,tcp,C#,Tcp,好的,我研究了一下异步TCP网络连接。我试着做了一个,但失败了。我想做的是确保服务器或客户端始终准备好接收聊天,并且能够随时发送聊天。我不希望它们处于备用模式 e、 g.当客户端等待接收时,服务器发送,因此客户端此时无法发送。 我不想那样 在Windows应用程序上执行此操作。一旦我连接,系统资源就快速增长到100%=/ 服务器代码 using System; using System.Collections.Generic; using System.ComponentModel; using

好的,我研究了一下异步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;
            }
        }
    }
}

问题是
while
在客户端方法
Connect
中循环。 删除它,因为它会无限循环,将CPU使用率提高到100%,而且毫无用处

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

  • 交叉线程操作异常
例如,在您的
Client.Recieve
方法中,您可以执行以下操作:
label1.Text=Encoding.ASCII.GetString(g_bmsg,0,g_bmsg.Length)
实际上,您正试图从另一个线程(侦听接收到的MSG的线程)设置标签文本,这是不允许的
这样做:

为标签文本创建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评论:

对于什么是线程的一个好的和广泛的解释,看它的

总之,简单地说,一个正在运行的进程包含一个或多个线程,这些线程是可以并发执行的代码的一部分

从您的TCP示例开始,使用
Socket.Receive
而不是
Socket.BeginReceive
可以阻止对
Socket.Receive()
调用的执行(我的意思是包含
Receive
方法的代码行后面的代码行将无法到达),直到接收到某个内容为止

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

相反,使用
Socket.BeginReceive
,在幕后创建一个新线程。该线程可能调用并停止Socket.Receive方法,一旦收到某个内容,它就会调用作为参数传递的方法

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


因此,当您更改
label.Text
时,实际上是从另一个线程设置它:由
Socket.BeginReceive
创建的线程。我快速查看了此代码,并从以下建议开始

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


然后,您还应该注意从回调方法更新UI控件,因为回调在非UI线程上运行,这可能会导致大量问题。您应该考虑使用
Control.Invoke
Control.BeginInvoke
将请求封送回UI线程,然后UI线程可以更新控件

你听说过评论你的代码吗?塞萨尔:没必要在这里粗鲁无礼。好的代码是自解释的代码,不需要一堆注释来解释代码的功能。贴出的代码看起来很直接,所以我很难看到你遗漏了什么评论?对不起,如果我听起来很粗鲁;那不是我的本意。我想帮忙,但由于没有评论而感到沮丧。我不认为代码是不言自明的,因此我的评论也是如此。很抱歉,如果代码作者不首先帮助我,我将无法提供帮助。:-)我犯这个错误的原因是因为我还没有学会穿线哈哈。可以解释什么是线程,以及你如何知道这是一个不同的线程?