private static LinkedList<TcpClient> clientList = new LinkedList<TcpClient>();
// Process the client connection.public static void DoAcceptTcpClientCallback(IAsyncResult ar) {
// Get the listener that handles the client request.
TcpListener listener = (TcpListener) ar.AsyncState;
// End the operation and display the received data on the console.
TcpClient client = listener.EndAcceptTcpClient(ar);
clientList.AddLast(client);
// Process the connection here. (Add the client to a server table, read data, etc.)
NetworkStream clientStream = client.GetStream();
Console.WriteLine("Client connected completed. Total clients:" + clientList.Count);
// Signal the calling thread to continue.
//tcpClientConnected.Set();
}
Вся проблема в том, что DoAcceptTcpClientCallback срабатывает только один раз =( А как перехватить момент подключение к серверу и момент отключения от сервера?
Здравствуйте, EyfelFenk, Вы писали:
EF>не ругайте сильно.
А несильно можно?
EF>Вся проблема в том, что DoAcceptTcpClientCallback срабатывает только один раз =( А как перехватить момент подключение к серверу и момент отключения от сервера?
Чтобы сработало больше одного раза, надо вызвать больше одного раза А чтобы "поймать" момент отключения клиента, сокет должен находиться в состоянии передачи или приёма.
Да вот Вам упрощённый пример эхо-сервера, как раз вчера набросал для теста.
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace SrvTcpListener
{
class ServerProgram
{
class AsyncData
{
public Socket s;
public byte[] Buf;
}
static volatile bool _сlose;
static AsyncCallback acptCallback = new AsyncCallback((ar) =>
{
var listener = (TcpListener)ar.AsyncState;
if (_сlose) return;
listener.BeginAcceptSocket(acptCallback, listener);
var socket = listener.EndAcceptSocket(ar);
AsyncData data = new AsyncData();
data.s = socket;
data.Buf = new byte[256];
socket.BeginReceive(data.Buf, 0, data.Buf.Length,
SocketFlags.None, recvCallback, data);
});
static AsyncCallback recvCallback = new AsyncCallback((ar) =>
{
SocketError err;
AsyncData data = (AsyncData)ar.AsyncState;
int Len = data.s.EndReceive(ar, out err);
if (Len > 0 && err == SocketError.Success)
{
data.s.BeginSend(data.Buf, 0, Len, SocketFlags.None, (ar2) =>
{
SocketError err2;
int Len2 = data.s.EndSend(ar2, out err2);
if (Len2 > 0 && err2 == SocketError.Success)
data.s.BeginReceive(data.Buf, 0, data.Buf.Length,
SocketFlags.None, recvCallback, data);
else// Клиент потерян
data.s.Close();
}, null);
}
else// Клиент потерян
data.s.Close();
});
static void Main(string[] args)
{
IPAddress addr = IPAddress.Parse("127.0.0.1");
var listener = new TcpListener(addr, 7777);
_сlose = false;
listener.Start();
listener.BeginAcceptSocket(acptCallback, listener);
Console.WriteLine("Start");
Console.ReadKey();
_сlose = true;
listener.Stop();
Console.WriteLine("Stop");
Thread.Sleep(1);
}
}
}
JR>Чтобы сработало больше одного раза, надо вызвать больше одного раза А чтобы "поймать" момент отключения клиента, сокет должен находиться в состоянии передачи или приёма.
Спасибо. Но вот когда мой клиент пытается отправить и получить данные от сервера:
try {
connectToServer();
NetworkStream serverStream = clientSocket.GetStream();
CommandLogin cmd = new CommandLogin();
cmd.write(name, Encoding.UTF8);
cmd.write(password, Encoding.UTF8);
//Отправка командыbyte[] outStream = cmd.generateAnswer();
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
Console.WriteLine("КЛИЕНТ: отправили " + outStream);
//Получение ответаbyte[] inStream = new byte[10025];
serverStream.Read(inStream, 0, (int)clientSocket.ReceiveBufferSize);
Console.WriteLine("КЛИЕНТ: получили " + outStream);
serverStream.Close();
disconnectFromServer();
/*IUser user = ServerObject.ExecuteLogin(name, password);
if (user != null) {
return user;
} else {
throw new BadUserLogin("Введенные данные не верны. Повторите попытку.");
}*/return null;
} catch (SocketException error) {
throw new ServerResponseException("Неудалось подключиться к серверу:" + error.Message);
}
Выпадает ошибка:
An unhandled exception of type 'System.ObjectDisposedException' occurred in System.dll
Additional information: Доступ к ликвидированному объекту невозможен.
JR>Чтобы сработало больше одного раза, надо вызвать больше одного раза А чтобы "поймать" момент отключения клиента, сокет должен находиться в состоянии передачи или приёма.
И не совысем понятно, где окончание приема от клиента =(
То есть мне надо, чтобы сервер по завершению приема данных от данного клиента выполнил определенную команду, и после ее завершения отправил ответ клиенту.
Здравствуйте, EyfelFenk, Вы писали:
EF>То есть мне надо, чтобы сервер по завершению приема данных от данного клиента выполнил определенную команду, и после ее завершения отправил ответ клиенту.
Ну так и в чём проблема-то, я не понимаю
static AsyncCallback recvCallback = new AsyncCallback((ar) =>
{
SocketError err;
AsyncData data = (AsyncData)ar.AsyncState;
int Len = data.s.EndReceive(ar, out err);
if (Len > 0 && err == SocketError.Success)
{
// Вот здесь Вы получили данные от клиента, Len - число байт,
// здесь, убедившись, что запрос получен полностью, и обрабатывайте, записывайте
// ответ в буфер и отправляйте следующим вызовом
data.s.BeginSend(data.Buf, 0, Len, SocketFlags.None, (ar2) =>
{
SocketError err2;
int Len2 = data.s.EndSend(ar2, out err2);
if (Len2 > 0 && err2 == SocketError.Success)
data.s.BeginReceive(data.Buf, 0, data.Buf.Length,
SocketFlags.None, recvCallback, data);
else// Клиент потерян
// Сюда(либо ниже) попадёте, если клиент отключился
// либо обнаружились проблемы со связью
// Если у Вас Err == Success, а Len == 0, то это означает, что клиент отключился.
// Если Err != Success, то имели место какие-то проблемы,
// подавляющее большинство из которых - фатальные,
// и требуют немедленного закрытия сокета.
data.s.Close();
}, null);
}
else// Клиент потерян
data.s.Close();
});
Предвидя вопрос, пара слов о размере пакета. Сервер должен либо знать этот размер, либо уметь определить его конец, чтобы быть уверенным в получении его целиком. Это определяется протоколом. Например, некоторые протоколы использует в качестве метки конца строку "/r/n/r/n". Или можно передатьразмер от клиента первыми байтами пакета. Какой способ выбрать, решать придётся Вам, но что-то должно быть обязательно.
Здравствуйте, Jolly Roger, Вы писали:
JR>Предвидя вопрос, пара слов о размере пакета. Сервер должен либо знать этот размер, либо уметь определить его конец, чтобы быть уверенным в получении его целиком. Это определяется протоколом. Например, некоторые протоколы использует в качестве метки конца строку "/r/n/r/n". Или можно передатьразмер от клиента первыми байтами пакета. Какой способ выбрать, решать придётся Вам, но что-то должно быть обязательно.
Спасибо большое за разъяснение детальное. А то после Java с колбаками туплю еще =(
static AsyncCallback acceptSocketCallback = new AsyncCallback((ar) => {
Array values = (Array)ar.AsyncState;
var listener = (TcpListener)values.GetValue(0);
if (_сlose) {
return;
}
listener.BeginAcceptSocket(acceptSocketCallback, values);
Вопрос в том, как определить новые это коннект от клиента или нет, просто если я два раза один клиентом посылаю команду, то он каждый раз заного создает все это конструкцию, а мне надо сохранять в список текущий подключний, чтобы при необходимости можно было отправить данные ему. Как это сделать? =(