2016-11-18 5 views
-1

Я новичок в C#, и я хочу написать чат-программу, используя поток, в котором есть две нити в каждой стороне (одна для прослушивания и другая для отправки сообщений).Приложение для чата Windows Form Использование потоков

Однако потоки не могут работать с компонентами (то есть с текстовым полем), потому что другой поток уже поймал это.

Вот мои коды:

стороне сервера:

private TcpClient clientSocket; 

private void btnStartListening_Click(object sender, EventArgs e) 
{ 
    TcpListener serverSocket = new TcpListener(int.Parse(txtPort.Text)); 
    int requestCount = 0; 
    clientSocket = default(TcpClient); 
    serverSocket.Start(); 
    txtReceivedMessages.Text +=" >> Server Started\n"; 
    clientSocket = serverSocket.AcceptTcpClient(); 
    txtReceivedMessages.Text += " >> Accept connection from client\n"; 
    requestCount = 0; 
    Thread t = new Thread(listeningThread);   
    t.Start();  
    clientSocket.Close(); 
    serverSocket.Stop();      
} 

private void listeningThread() 
{ 
    while (true) 
    { 
     try 
     {     
      NetworkStream networkStream = clientSocket.GetStream(); 
      byte[] bytesFrom = new byte[10025]; 
      networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize); 
      string dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom); 
      dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));     
      string serverResponse = "Last Message from client" + dataFromClient;     
      networkStream.Flush();     
      txtReceivedMessages.Text += dataFromClient; 
      txtReceivedMessages.Update(); 
     } 
     catch (Exception ex) 
     { 
      txtReceivedMessages.Text += ex.ToString() + "\n"; 
     } 
    } 
} 

private void btnSend_Click(object sender, EventArgs e) 
{ 
    NetworkStream networkStream = clientSocket.GetStream(); 
    Byte[] data = Encoding.ASCII.GetBytes(txtSendMessage.Text); 
    networkStream.Write(data, 0, data.Length); 
} 

enter image description here

стороне клиента:

private NetworkStream serverStream; 
private void btnSend_Click(object sender, EventArgs e) 
{ 
    serverStream = clientSocket.GetStream(); 
    byte[] outStream = System.Text.Encoding.ASCII.GetBytes(txtMsgToSend.Text + "$"); 
    serverStream.Write(outStream, 0, outStream.Length); 
    serverStream.Flush(); 


} 

private void listeningThread() 
{ 
    byte[] inStream = new byte[10025]; 
     serverStream.Read(inStream, 0, (int)clientSocket.ReceiveBufferSize); 
     string returndata = System.Text.Encoding.ASCII.GetString(inStream); 
     txtReceivedMsgs.Text += ">>" + returndata; 
     txtMsgToSend.Text = ""; 
     txtMsgToSend.Focus(); 
} 

private void btnSetConfiguration_Click(object sender, EventArgs e) 
{ 
    txtReceivedMsgs.Text += "Client Started"; 
    clientSocket.Connect(txtIP.Text, int.Parse(txtPort.Text)); 
    lblStatus.Text = "Client Socket Program - Server Connected ..."; 
} 

enter image description here

+2

Вы не задали вопрос. – Servy

+0

вам нужно исследовать методы 'Invoke' или' BeginInvoke'. Они могут выполнить код, который бросает исключение в основной поток, который создал элемент управления, который вы хотите манипулировать, например, TextBox. –

+0

. Уважаемый @Mong Zhu. У меня есть ошибка в первой строке функции listenThread (на стороне сервера): Не удается получить доступ к расположенному объекту. – fateme

ответ

0

Если вы хотите обновить элементы управления форматом из другого потока, вам нужно использовать метод Invoke().

Смотреть еще с этого поста на SO How to update the GUI from another thread in C#?

редактирование: Материалы из связанного поста

Для .NET 2.0, вот хороший бит кода, который я написал, что делает именно то, что вы хотите, и работает любое имущество, на контроле:

private delegate void SetControlPropertyThreadSafeDelegate(
         Control control, 
         string propertyName, 
         object propertyValue); 

public static void SetControlPropertyThreadSafe(
         Control control, 
         string propertyName, 
         object propertyValue) 
{ 
    if (control.InvokeRequired) 
    { 
    control.Invoke(new SetControlPropertyThreadSafeDelegate    
    (SetControlPropertyThreadSafe), 
    new object[] { control, propertyName, propertyValue }); 
    } 
    else 
    { 
    control.GetType().InvokeMember(
    propertyName, 
    BindingFlags.SetProperty, 
    null, 
    control, 
    new object[] { propertyValue }); 
    } 
} 

вызовов это так:

// thread-safe equivalent of 
// myLabel.Text = status; 
SetControlPropertyThreadSafe(myLabel, "Text", status); 

Если вы используете .NET 3.0 или выше, вы можете переписать выше метод как метод расширения класса Control, который затем упростить вызов:

myLabel.SetPropertyThreadSafe("Text", status); 

UPDATE 05/10/2010 :

для .NET 3.0 вы должны использовать этот код:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
          Control @this, 
          Expression<Func<TResult>> property, 
          TResult value); 

public static void SetPropertyThreadSafe<TResult>(
          this Control @this, 
          Expression<Func<TResult>> property, 
          TResult value) 
{ 
    var propertyInfo = (property.Body as MemberExpression).Member 
    as PropertyInfo; 

    if (propertyInfo == null || 
     [email protected].GetType().IsSubclassOf(propertyInfo.ReflectedType) || 
     @this.GetType().GetProperty(
     propertyInfo.Name, 
     propertyInfo.PropertyType) == null) 
    { 
    throw new ArgumentException("The lambda expression 'property' must  reference a valid property on this Control."); 
    } 

    if (@this.InvokeRequired) 
    { 
    @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> 
    (SetPropertyThreadSafe), 
    new object[] { @this, property, value }); 
    } 
    else 
    { 
     @this.GetType().InvokeMember(
     propertyInfo.Name, 
     BindingFlags.SetProperty, 
     null, 
     @this, 
     new object[] { value }); 
    } 
} 

, который использует LINQ и лямбда-выражения, чтобы значительно чище, проще и безопаснее синтаксис:

myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile 

не только имя свойства в настоящее время проверяются во время компиляции, тип свойства как хорошо, так что это невозможно (например) назначить строковое значение логического свойства, и, следовательно, вызвать исключение во время выполнения.

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

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false); 

Поэтому я добавил проверки во время выполнения, чтобы убедиться, что прошло -In свойство действительно принадлежит элементу управления, на который вызывается метод. Не идеально, но все же намного лучше, чем версия .NET 2.0.

Если у кого-либо есть дополнительные предложения по улучшению этого кода для безопасности во время компиляции, прокомментируйте!

+0

Хотя эта ссылка может ответить на вопрос, лучше включить здесь основные части ответа и предоставить ссылку для справки. Ответные ссылки могут стать недействительными, если связанная страница изменится. - [Из обзора] (/ review/low-quality-posts/14335410) – Mark

+0

Я чувствую, что со мной говорил, что ему нужно использовать метод Invoke? Я могу отредактировать свой ответ. –

+0

Затем отредактируйте свой ответ – Danh

Смежные вопросы