Простое асинхронное приложение для TCP-чата [C #] - PullRequest
1 голос
/ 10 октября 2010

Хорошо, я немного изучил асинхронное сетевое соединение TCP.Я пытался сделать один, но не получилось.Что я хочу сделать, так это убедиться, что сервер или клиент всегда готовы принять чат и могут отправлять чат в любое время.Я не хочу, чтобы они были в альтернативном режиме.

например, отправка сервером, пока клиент ожидает получения, поэтому клиент не может отправить в это время.Я не хочу этого!

Сделал это в приложении 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 ]

1 голос
/ 10 октября 2010

Я быстро взглянул на этот код, и я бы начал со следующего предложения:

Удалите зацикливание из вашего Accept обратного вызова, просто запустите BeginReceive и позвольте ему быть.Затем в вашем Receive методе вы можете просто инициировать следующий BeginReceive.Это применимо как к клиентскому, так и к серверному коду, за исключением, конечно, клиентского кода, который вы удалите цикл из вашего Connect метода обратного вызова.

Затем вы также должны следить за обновлением элементов управления пользовательского интерфейса изметоды обратного вызова, поскольку обратный вызов выполняется в потоке, не связанном с пользовательским интерфейсом, что может вызвать массу проблем.Вам следует рассмотреть использование Control.Invoke или Control.BeginInvoke для перенаправления запроса обратно в поток пользовательского интерфейса, который затем может обновить элементы управления.

1 голос
/ 10 октября 2010

Ну, проблема в цикле while в клиентском методе Connect. Удалите его, потому что он зацикливается, увеличивая нагрузку на процессор до 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;
}

затем используйте сеттер вместо прямого вызова 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...