TCP聊天客户端/服务器无法解决localhost问题

时间:2015-02-14 00:26:21

标签: c# windows chat server

我目前正在学习编写使用服务器的聊天室应用程序。到目前为止,如果我在一台机器上运行服务器和应用程序的多个实例,一切正常。当我尝试在一台计算机上运行服务器而在另一台计算机上运行实际的聊天应用程序时,我得到一个异常,它读取"连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败因为连接的主机无法响应(Ipaddress)(端口)"

服务器端代码:

using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Collections;
using System.Text;
using System.Threading;

namespace ChatAppServer
{
    class Program
    {
        public static Hashtable ClientList = new Hashtable(); 
        const int PORT = 321;
        string localIp;


        static void Main(string[] args)
        {


            TcpListener sckServer = new TcpListener(PORT);
            TcpClient sckClient = default(TcpClient);
            int counter = 0;

            sckServer.Start();
            Console.WriteLine("Chat Server is now Running ....");
            counter = 0;
            //Parser myParser = new Parser();
            while (true)
            {
                counter = counter + 1;
                sckClient = sckServer.AcceptTcpClient();

                string clientData = "";
                byte[] recieveData = new byte[10025]; 

                NetworkStream netStream = sckClient.GetStream();
                netStream.Read(recieveData, 0, (int)sckClient.ReceiveBufferSize);
                clientData = System.Text.Encoding.ASCII.GetString(recieveData);
                clientData = clientData.Substring(0, clientData.IndexOf("$"));

                ClientList.Add(clientData, sckClient);

                Broadcast(clientData + " joined the chat", clientData, false);

                Console.WriteLine(clientData + " connected to the chat");
                handleClient client = new handleClient();
                client.ClientStart(sckClient, clientData, ClientList);
            }
            sckClient.Close();
            sckServer.Stop();
            Console.WriteLine("exit");
            Console.ReadLine();
        }


        public static void Broadcast(string msg, string userName, bool flag)
        {
            foreach (DictionaryEntry Item in ClientList)
            {
                TcpClient sckBroadcast;
                sckBroadcast = (TcpClient)Item.Value;
                NetworkStream broadcastStream = sckBroadcast.GetStream();
                Byte[] broadcastData = null;

                if (flag == true)
                {
                    broadcastData = Encoding.ASCII.GetBytes(userName + ": " + msg);
                }
                else 
                {
                    broadcastData = Encoding.ASCII.GetBytes(msg);
                }
                broadcastStream.Write(broadcastData, 0, broadcastData.Length);
                broadcastStream.Flush();
            }
        }

        public class handleClient
        {
            TcpClient sckClient;
            string clId;
            Hashtable ClientList;

            public void ClientStart(TcpClient inSckClient, string clientId, Hashtable clist) {
                this.sckClient = inSckClient;
                this.clId = clientId;
                this.ClientList = clist;
                Thread ctThread = new Thread(runChat);
                ctThread.Start();
            }
            private void runChat() {
                int requestCount = 0;
                byte[] recieveData = new byte[10025];
                string clientData = "";
                string rCount = null;

                while ((true))
                {
                    try
                    {
                        requestCount += 1;
                        NetworkStream netStream = sckClient.GetStream();
                        netStream.Read(recieveData, 0, (int)sckClient.ReceiveBufferSize);
                        clientData = System.Text.Encoding.ASCII.GetString(recieveData);
                        clientData = clientData.Substring(0, clientData.IndexOf("$"));
                        Console.WriteLine(clId + " : " + clientData);
                        rCount = Convert.ToString(requestCount);

                        Program.Broadcast(clientData, clId, true);


                    }
                    catch(Exception ex) 
                    {
                        Console.WriteLine(ex.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;
//Need for the application
using System.Net;
using System.Net.Sockets;
using System.Threading;


namespace ChatApp
{
    public partial class Form1 : Form
    {
        System.Net.Sockets.TcpClient sckClient = new System.Net.Sockets.TcpClient();
        NetworkStream svrStream = default(NetworkStream);
        string recieveData = null;

        public Form1()
        {
            InitializeComponent();
            btnSend.Enabled = false;
        }
        private void btnConnect_Click(object sender, EventArgs e)
        {
            recieveData = "Connected to Server";
            msg();
            int serverPort = Convert.ToInt32(txtServerPort.Text);
            sckClient.Connect(txtServerIp.Text, serverPort);
            svrStream = sckClient.GetStream();

            byte[] outStream = System.Text.Encoding.ASCII.GetBytes(txtUserName.Text + "$");
            svrStream.Write(outStream, 0, outStream.Length);
            svrStream.Flush();

            Thread ctThread = new Thread(MessageCallBack);
            btnSend.Enabled = true;
            btnConnect.Enabled = false;
            txtUserName.Enabled = false;
            txtServerIp.Enabled = false;
            txtServerPort.Enabled = false;
            ctThread.Start();

        }

        private void MessageCallBack()
        {
            while(true)
            {
                svrStream = sckClient.GetStream();
                int buffSize = 0;
                byte[] inStream = new byte[10025];
                buffSize = sckClient.ReceiveBufferSize;
                svrStream.Read(inStream, 0, buffSize);
                string returnData = System.Text.Encoding.ASCII.GetString(inStream);
                recieveData = "" + returnData;
                msg();
            }
        }
        //function to display data strings 
        private void msg()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(msg));
            }
            else
            {
                lstMessage.Items.Add(recieveData);
            }
        }



        private void btnSend_Click(object sender, EventArgs e)
        {
            byte[] outStream = System.Text.Encoding.ASCII.GetBytes(txtMessage.Text + "$");
            svrStream.Write(outStream, 0, outStream.Length);
            svrStream.Flush();
            txtMessage.Text = "";
        }

1 个答案:

答案 0 :(得分:0)

我的代码有两个问题:

  • 不要使用.ReceiveBufferSize值,因为它是字节数组长度的不同值。并且您可以使索引超出范围异常。

  • 您在服务器端遇到并发问题。超过1个主题尝试访问ClientList,此集合非线程安全

要解决此问题,您可以使用lock关键字

private static object _lock = new object();
//...
lock (_lock)
{
     ClientList.Add(clientData, sckClient);
}

lock (_lock)
{
     Broadcast(clientData + " joined the chat", clientData, false);
}
//...
lock (_lock)
{
     Program.Broadcast(clientData, clId, true);

}

由于你是初学者,我会给你一些提示。

  • 尝试使用异步网络功能(优于原始线程),an example there
  • C#中有很多关于安全性的教程,有些东西比lock关键字更好。