1

ゲームのチャットサービスを作っていますが、

アカウント情報のクライアントであるTCPリスナー、ある種のログインサービスを使用しています。クライアントをサーバーに接続したままにして、彼がまだオンラインであるかどうかを確認し、新しいメッセージがある場合はメッセージを送信し続けることができるかどうか疑問に思っています。

ログインキューのソケットのリストを作成しようとしましたが、新しいソケットを受け入れるとすぐに、サーバーへの以前のソケットが切断されました。

byte[] usernameByte = new byte[100];
int usernameRecieved = s.Receive(usernameByte);
//guiController.setText(System.DateTime.Now + " Recieved Login...");

byte[] passByte = new byte[100];
int passRecieved = s.Receive(passByte);
//guiController.setText(System.DateTime.Now + " Recieved Password...");

string username = "";
string password = "";

for (int i = 0; i < usernameRecieved; i++)
     username += (Convert.ToChar(usernameByte[i]));

for (int i = 0; i < passRecieved; i++)
     password += (Convert.ToChar(passByte[i]));

if (DomainController.getInstance().checkAccount(username, password))
{
    ASCIIEncoding asen = new ASCIIEncoding();
    s.Send(asen.GetBytes("true"));
    s.Send(asen.GetBytes("U are succesfully logged in, press enter to continue"));
    guiController.setText(serverName,System.DateTime.Now+"");
    guiController.setText(serverName, "Sent Acknowledgement - Logged in");
}
else
{
    ASCIIEncoding asen = new ASCIIEncoding();
    s.Send(asen.GetBytes("false"));
    s.Send(asen.GetBytes("U are NOT logged in, press enter to continue"));
    guiController.setText(serverName, System.DateTime.Now + "");
    guiController.setText(serverName, "\nSent Acknowledgement - Not logged in");
}

これは、ユーザーから送信されたアカウント情報を確認するために現在使用しているコードです。これを送信した直後に、ユーザーは接続を切断し、次の接続に進みます。

別々のソケットのリストを1つ作成し、それらを1つずつ処理しようとしましたが、接続を試みたのは2つの異なるマシンであったとしても、前のソケットの接続が切断されたため失敗しました。

プログラムにすべての接続を維持させるために使用できるソリューション/ソケットを保存する方法を誰かが持っていますか?だから私はユーザー1からユーザー2にメッセージを送信し、彼らが接続したソケットを使用することができますか?または、接続するたびにIDを追加する必要がありますか?

編集

クライアントコード:(これは単なるテストクライアントです)

while (true)
{
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
tcpclnt.Connect("xx.xxx.xxx.xx", 26862);
// use the ipaddress as in the server program

while(!(checkResponse(tcpclnt.GetStream())))
{
     Thread.Sleep(1000);
}

Console.WriteLine("Connected");

Console.Write("Enter the string to be transmitted : ");

String str = Console.ReadLine();
if (str == "")
{
    str = " ";
}
                    
Stream stm = tcpclnt.GetStream();

ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(str);


Console.WriteLine("Transmitting.....");

stm.Write(ba, 0, ba.Length);

Console.Write("Enter the string to be transmitted : ");

String str2 = Console.ReadLine();
if (str2 == "")
{
   str2 = " ";
}

Stream stm2 = tcpclnt.GetStream();

ASCIIEncoding asen2 = new ASCIIEncoding();
byte[] ba2 = asen2.GetBytes(str2);


Console.WriteLine("Transmitting.....");

stm.Write(ba2, 0, ba2.Length);
if (str == "false")
{
    blijvenWerken = false;
}
byte[] bb = new byte[100];
int k = stm.Read(bb, 0, 100);

for (int i = 0; i < k; i++)
Console.Write(Convert.ToChar(bb[i]));

byte[] bb2 = new byte[100];
int k2 = stm.Read(bb2, 0, 100);
Console.Write("\n");
for (int i = 0; i < k2; i++)
Console.Write(Convert.ToChar(bb2[i]));

Console.WriteLine("\n");

tcpclnt.Close();
Thread.Sleep(1000);
}

ソケットを取得するサーバー:このコードはloginserverにあります。これは、接続を維持するために毎回1つのソケットしか受け入れられないため、queueCountを最大1に設定します。ユーザーアカウントに追加するために受け入れたソケット。

while (loginServerOn)
{
    if (queueCount < 1)
    {
        if (loginServer.getLoginListener().Pending())
        {
            loginQueue.Add(loginServer.getSocket());
            ASCIIEncoding asen = new ASCIIEncoding();
            Socket s = loginQueue.First();
            try
            {
                s.Send(asen.GetBytes("true"));
                queueCount++;
            }
            catch
            {
                loginQueue.Remove(s);
            }
        }
    }
}

受け入れられたソケットを返す関数。

public Socket getSocket()
{
    return myList.AcceptSocket();
}

編集:質問の本質

受け取ったsockedまたはclientをAccountオブジェクトに追加したいので、すべての接続にリンクされたアカウントがあります。特定のアカウントにメッセージを送信する場合は、そのアカウントにバインドされたsockedまたはclientにメッセージを送信する必要があります。 、私がこれを達成する方法を教えてくれますか?

4

2 に答える 2

1

これはまだc#とソケットですが、私のアプローチはあなたのアプローチとは異なります。

私は、あなたがアカウントと呼んでいるものと目的が似ている「connectedCleint」の概念を採用しました。

ソケット接続の受け入れとトップレベルの管理を担当するServerTerminalというクラスがあります。これで私は持っています:

 public Dictionary<long, ConnectedClient> DictConnectedClients =
        new Dictionary<long, ConnectedClient>();

これが、sockethandleによってインデックス付けされた接続済みクライアントのリストです。

接続を受け入れるために、私はルーチンを持っています:

public void StartListen(int port)
    {
        socketClosed = false;
        IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);

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

        //bind to local IP Address...
        //if ip address is allready being used write to log
        try
        {
            listenSocket.Bind(ipLocal);
        }
        catch (Exception excpt)
        {
            // Deal with this.. write your own log code here ?
            socketClosed = true;

            return;
        }
        //start listening...

        listenSocket.Listen(100); // Max 100 connections for my app

        // create the call back for any client connections...
        listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);

    }

したがって、クライアントが接続すると、次のように起動します。

private void OnClientConnection(IAsyncResult asyn)
    {
        if (socketClosed)
        {
            return;
        }

        try
        {
            Socket clientSocket = listenSocket.EndAccept(asyn);

            ConnectedClient connectedClient = new ConnectedClient(clientSocket, this, _ServerTerminalReceiveMode);

            //connectedClient.MessageReceived += OnMessageReceived;
            connectedClient.Disconnected += OnDisconnection;
            connectedClient.dbMessageReceived += OndbMessageReceived;

            connectedClient.ccSocketFaulted += ccSocketFaulted;

            connectedClient.StartListening();

            long key = clientSocket.Handle.ToInt64();
            if (DictConnectedClients.ContainsKey(connectedClient.SocketHandleInt64))
            {
                // Already here - use your own error reporting..
            }

            lock (DictConnectedClients)
            {
                DictConnectedClients[key] = connectedClient;
            }

            // create the call back for any client connections...
            listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);

        }
        catch (ObjectDisposedException excpt)
        {
            // Your own code here..
        }
        catch (Exception excpt)
        {
            // Your own code here...
        }

    }

あなたにとってこれの重要な部分は次のとおりです。

            // create the call back for any client connections...
            listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);

これにより、サーバー端末が新しい接続を受信するように設定されます。

編集:接続されたクライアントのバージョンを削減します:

public class ConnectedClient
{
    private Socket mySocket;
    private SocketIO mySocketIO;

    private long _mySocketHandleInt64 = 0;


    // These events are pass through; ConnectedClient offers them but really
    // they are from SocketIO

    public event TCPTerminal_ConnectDel Connected
    {
        add
        {
            mySocketIO.Connected += value;
        }
        remove
        {
            mySocketIO.Connected -= value;
        }
    }

    public event TCPTerminal_DisconnectDel Disconnected
    {
        add
        {
            mySocketIO.Disconnected += value;
        }
        remove
        {
            mySocketIO.Disconnected -= value;
        }
    }

    // Own Events

    public event TCPTerminal_TxMessagePublished TxMessageReceived;

    public delegate void SocketFaulted(ConnectedClient cc);
    public event SocketFaulted ccSocketFaulted;

    private void OnTxMessageReceived(Socket socket, TxMessage myTxMessage)
    {
     // process your message
    }



    private void OnMessageSent(int MessageNumber, int MessageType)
    {
    // successful send, do what you want..
    }





    public ConnectedClient(Socket clientSocket, ServerTerminal ParentST)
    {
        Init(clientSocket, ParentST, ReceiveMode.Handler);
    }

    public ConnectedClient(Socket clientSocket, ServerTerminal ParentST, ReceiveMode RecMode)
    {
        Init(clientSocket, ParentST, RecMode);
    }

    private void Init(Socket clientSocket, ServerTerminal ParentST, ReceiveMode RecMode)
    {
        ParentServerTerminal = ParentST;
        _myReceiveMode = RecMode;

        _FirstConnected = DateTime.Now;
        mySocket = clientSocket;
        _mySocketHandleInt64 = mySocket.Handle.ToInt64();
        mySocketIO = new SocketIO(clientSocket, RecMode);

        // Register for events
        mySocketIO.TxMessageReceived += OnTxMessageReceived;
        mySocketIO.MessageSent += OnMessageSent;
        mySocketIO.dbMessageReceived += OndbMessageReceived;

    }


    public void StartListening()
    {
        mySocketIO.StartReceiving();
    }

    public void Close()
    {
        if (mySocketIO != null)
        {
            mySocketIO.Close();
            mySocketIO = null;
        }

        try
        {
            mySocket.Close();
        }
        catch
        {
            // We're closing.. don't worry about it
        }
    }

    public void SendMessage(int MessageNumber, int MessageType, string Message)
    {
        if (mySocket != null && mySocketIO != null)
        {
            try
            {
                mySocketIO.SendMessage(MessageNumber, MessageType, Message);
            }
            catch
            {
                // mySocketIO disposed inbetween check and call
            }
        }
        else
        {
            // Raise socket faulted event
            if (ccSocketFaulted != null)
                ccSocketFaulted(this);
        }
    }


}

}

いくつかの便利なリンク:

これが私が始めたところです:http: //vadmyst.blogspot.com.au/2008/01/how-to-transfer-fixed-sized-data-with.html

http://vadmyst.blogspot.com.au/2008/03/part-2-how-to-transfer-fixed-sized-data.html

と..

C#ソケットとマルチスレッド

接続されたソケットに.BeginReceiveの直後に新しいメッセージを受け入れさせますか?

http://nitoprograms.blogspot.com.au/2009/04/tcpip-net-sockets-faq.html

http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod

現在、ソリューション全体を投稿することはできません。デバッグする必要のあるサーバーコードに欠陥があります。さらに、私の雇用主が公開したくない部分があります。しかし、私はVadymが可変長メッセージに対して持っていたものに基づいてコードを作成しました。

于 2013-01-16T00:50:13.083 に答える
0

サーバーがTCP接続を受け入れる準備ができると、サーバーは新しいTCPソケットを作成し、それをポートにBind()して、Listen()メソッドを使用します。接続要求が着信すると、Listen()メソッドは、サーバーとクライアントが通信に使用する新しいソケットを返します。サーバーとクライアントは、この時点でSend()とReceive()を使用してデータをやり取りできます。クライアントが切断すると、サーバーのReceive()は0バイトのデータで終了します。

最初の接続を受け入れた後(つまり、最初のクライアントと対話している間)に別の接続要求を待ちたい場合は、これを行うことができます。この時点で、複数の接続を処理できるように、スレッドや非同期メソッドなどを使用する必要があります。基本的に、リスニングソケットからの接続要求をAccept()することができます。

マイク

于 2013-01-15T22:37:57.453 に答える