Приложение службы чата
Я делаю чат-сервис для игры,
Я использую слушатель 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, и просто использовать сокет, с которым они связаны? или мне нужно добавить идентификатор каждый раз, когда они устанавливают соединение?
РЕДАКТИРОВАТЬ
Код клиента: (это всего лишь тестовый клиент)
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);
}
Сервер получает сокеты: этот бит кода находится на сервере входа в систему, потому что я могу принимать только 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();
}
РЕДАКТИРОВАТЬ: Суть вопроса
Я хочу добавить полученную учетную запись или клиента, полученную в мой объект Учетная запись, поэтому каждое соединение имеет учетную запись, к которой она привязана. Когда я хочу отправить сообщение определенной учетной записи, оно должно отправить сообщение подключенному или клиенту, привязанному к этой учетной записи., можете ли вы помочь / показать мне, как я могу этого добиться?
2 ответа
Это все еще C# и сокеты, но мой подход отличается от вашего.
Я придерживался концепции "connectedCleint", которая по своему назначению похожа на то, что вы называете учетной записью.
У меня есть класс с именем ServerTerminal, который отвечает за принятие и управление уровнями соединений с сокетами. В этом я получил:
public Dictionary<long, ConnectedClient> DictConnectedClients =
new Dictionary<long, ConnectedClient>();
Итак, это мой список подключенных клиентов, проиндексированных дескриптором сокета.
Чтобы принимать соединения, у меня есть процедура:
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
А также..
Заставить подключенный сокет принимать новые сообщения сразу после.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
Я не могу опубликовать все мое решение только сейчас; в коде моего сервера есть недостаток, который мне нужно отладить; плюс есть части, которые мой работодатель не хочет публиковать. Но я основал свой код на том, что имел Вадим для сообщений переменной длины.
Когда сервер готов принять TCP-соединения, он создает новый сокет TCP, связывает его с портом и использует метод Listen(). Когда приходит запрос на соединение, метод Listen() возвращает новый сокет, который сервер и клиент используют для связи. На этом этапе сервер и клиент могут передавать данные назад и вперед с помощью Send () и Receive(). Если клиент отключается, сервер Receive() завершает работу с 0 байтами данных.
Если вы хотите дождаться другого запроса на подключение, как только вы приняли первое подключение (т. Е. Во время взаимодействия с первым клиентом), это можно сделать. На этом этапе вам нужно использовать что-то вроде потоков или асинхронных методов, чтобы вы могли обрабатывать более одного соединения. По сути, вы сможете принимать запросы на подключение от Accept() из вашего прослушивающего сокета.
Майк