[c#] Подскажите по работе с сокетами

markmsk

Есть программа на дот.нете на с#, обращается к серверу через сокет.
Вопрос такой как отловить обрыв связи с сервером?
если нужно код выложу

evgen5555

try-catch на Send-Receive?

markmsk

Так стоит уже try catch, и не ловит обрыв связи с сервером, а если оборвать связь вообще с сетью, то ловит.

evgen5555

Тогда кусок кода с передачей данных пости

markmsk

rivate void EstablishSocket(Object state)
{
NetworkStream rstream = null;
NetworkStream wstream = null;

try
{
//RevisionNew.TransferTransport send = new RevisionNew.TransferTransport;
//String sendtext;
//String strid = "123";

Socket temp1 = new
Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
temp1.Blocking = true;
temp1.Connect(endPoint);

wsocket = temp1;
wstream = new NetworkStream(wsocket);
writer = new StreamWriter(wstream);

/*
TerminalInfo Info = new TerminalInfo;
//*
byte[] idtmp = Info.UniqueUnitID;
int id = 0;
for (int i = 0;i < idtmp.Length; i++)
{
id += (int)idtmp;
}

String strid = id.ToString;
*/
String strid = this.TerminalId;
//MessageBox.Show(strid);
RevisionNew.TransferTransport send = new RevisionNew.TransferTransport;
send.Command = "InitializeTerminalWriter";
send.Data = strid;
send.Type = "Command";
String sendtext = this.GetXML(send, send.GetType;
writer.Write(sendtext+(char)10);
//writer.Write("w!"+strid+"!"+(char)10);
writer.Flush;


// Try a client connection
// Для начала стартуем ридер
Socket temp = new
Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
temp.Blocking = true;
temp.Connect(endPoint);
rsocket = temp;
rstream = new NetworkStream(rsocket);

reader = new StreamReader(rstream);
// Encoding.UTF8
// потом добавим врайтер
TextWriter TempWrite =new StreamWriter(rstream);

send.Command = "InitializeTerminalReader";
sendtext = this.GetXML(send, send.GetType;
TempWrite.WriteLine(sendtext + (char)10);
TempWrite.Flush;

//String tmp1 = "test";
//writer.Write(tmp1);
//writer.Flush;
char[] handshake = new char[13];
//
String tmp = "";
try
{
tmp = reader.ReadLine;

//byte[] sss = BitConverter.GetBytes( tmp );



char[] a = tmp.ToCharArray;

if (!(tmp.Length > 0
{
rsocket.Close;
rsocket = null;
wsocket.Close;
wsocket = null;
}

}
catch
{
rsocket.Close;
rsocket = null;
wsocket.Close;
wsocket = null;
}


// If it all worked out, create stream objects
if(rsocket != null)
{
Notifications(Notification.Initialized, this);
//String tmp = System.Convert.ToString(handshake);
Notifications(Notification.ReceivedAppend, tmp);
// Start receiving talk
// Note: on w2k and later platforms, the NetworkStream.Read
// method called in ReceiveTalk will generate an exception when
// the remote connection closes. We handle this case in our
// catch block below.
ReceiveTalk;
// On Win9x platforms, NetworkStream.Read returns 0 when
// the remote connection closes, prompting a graceful return
// from ReceiveTalk above. We will generate a Notification.End
// message here to handle the case and shut down the remaining
// WinTalk instance.
Notifications(Notification.End, "Remote connection has closed.");
}
else
{
Notifications(Notification.Error,
"Failed to Establish Socket, did you specify the correct port?");
}
}
catch(IOException e)
{
SocketException sockExcept = e.InnerException as SocketException;
if(sockExcept != null && 10054 == sockExcept.ErrorCode)
{
Notifications(Notification.End, "Remote connection has closed.");
}
else
{
if (Notifications != null)
Notifications(Notification.Error, "Socket Error:\n"+e.Message);
}

}
catch(Exception e)
{
Notifications(Notification.Error, "Socket Error:\n"+e.Message);
}
}

evgen5555

Разрыв происходит в ReceiveTalk? Оно нормально исключения обрабатвыает?

markmsk

Нет, если оборвать связь с сервером, тоникакие исключения не выбрасываются, а если оборвать связь с сетью вообще, то выскакивает вот catch(IOException e) это исключение в EsteblishSocket

Marinavo_0507

Если ты ждёшь ответа от сервера, а его нет, то ты долго ждать будешь.
Используй тайм-ауты.

markmsk

А почему при обрыве сети исключение выскакивает сразу же?

Marinavo_0507

Я понятия не имею, как ты "обрываешь связь" и в какой момент.

evgen5555

Потому что блокирующая операция на сокете получает уведомление от драйвера и немедленно завершается с исключением.

markmsk

Обрываю локальное соединение с сетью.
В любой момент

markmsk

Нужно ли делать отдельный поток который регулярно делает запросы на сервер и в случае его недоступности выбрасывает исключение?

evgen5555

Можешь приделать таймаут к сокету следующим образом:


//Send operations will timeout of confirmation is not received within 1000 milliseconds.
sSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000);

markmsk

щас попробую

markmsk

Где лучше приделать?

evgen5555



Socket temp = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
temp.Blocking = true;
//Можно, к примеру, сюда привинтить :)
temp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000); temp.Connect(endPoint);
rsocket = temp;
rstream = new NetworkStream(rsocket);

Marinavo_0507

Обычно всё подвисает в receive, если сервер пропадает неожиданно.
Вот тут нужен внешний таймер, если конечно в вашем .net в библиотеки такого уже не спрятано.

markmsk

А какое потом исключение должно вылететь?

evgen5555

Там и на Receive есть таймер.
SocketOptionName.ReceiveTimeout называется.
Блокирующие сокеты - отстой

evgen5555

Это лучше всего по ходу выполнения нелегитимных действий смотреть

markmsk

Не ловит ):

evgen5555

А поподробнее?

markmsk

Вставил в разных вариантах в том числе и в том как ты рекомендовал.
Исключение не ловится.
может нужно другое исключение.
Обрыв связи должен отлавливаться в любой момент, а не только при соединении с сервером.

bleyman

может нужно другое исключение.

В смысле? У тебя что, исключение летит, но влом посмотреть какое именно?

markmsk

Не летит исклюбчение в том-то и дело, а нужно что бы летело

evgen5555

Опять же - пиши сюда код, будем смотреть.
Только в [code] [/code] обрамляй, а то читать курсив неудобно

markmsk



private void EstablishSocket(Object state)
{
NetworkStream rstream = null;
NetworkStream wstream = null;

try
{
//RevisionNew.TransferTransport send = new RevisionNew.TransferTransport;
//String sendtext;
//String strid = "123";

Socket temp1 = new
Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
temp1.Blocking = true;
temp1.Connect(endPoint);

wsocket = temp1;
wstream = new NetworkStream(wsocket);
writer = new StreamWriter(wstream);

/*
TerminalInfo Info = new TerminalInfo;
//*
byte[] idtmp = Info.UniqueUnitID;
int id = 0;
for (int i = 0;i < idtmp.Length; i++)
{
id += (int)idtmp[i];
}

String strid = id.ToString;
*/
String strid = this.TerminalId;
//MessageBox.Show(strid);
RevisionNew.TransferTransport send = new RevisionNew.TransferTransport;
send.Command = "InitializeTerminalWriter";
send.Data = strid;
send.Type = "Command";
String sendtext = this.GetXML(send, send.GetType;
writer.Write(sendtext+(char)10);
//writer.Write("w!"+strid+"!"+(char)10);
writer.Flush;


// Try a client connection
// Для начала стартуем ридер
Socket temp = new
Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
temp.Blocking = true;
temp.Connect(endPoint);
rsocket = temp;
rsocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000);//debug
rstream = new NetworkStream(rsocket);

reader = new StreamReader(rstream);
// Encoding.UTF8
// потом добавим врайтер
TextWriter TempWrite =new StreamWriter(rstream);

send.Command = "InitializeTerminalReader";
sendtext = this.GetXML(send, send.GetType;
TempWrite.WriteLine(sendtext + (char)10);
TempWrite.Flush;

//String tmp1 = "test";
//writer.Write(tmp1);
//writer.Flush;
char[] handshake = new char[13];
//
String tmp = "";
try
{
tmp = reader.ReadLine;


//byte[] sss = BitConverter.GetBytes( tmp );



char[] a = tmp.ToCharArray;

if (!(tmp.Length > 0
{
rsocket.Close;
rsocket = null;
wsocket.Close;
wsocket = null;
}

}
catch
{
rsocket.Close;
rsocket = null;
wsocket.Close;
wsocket = null;
}


// If it all worked out, create stream objects
if(rsocket != null)
{
Notifications(Notification.Initialized, this);
//String tmp = System.Convert.ToString(handshake);
Notifications(Notification.ReceivedAppend, tmp);
// Start receiving talk
// Note: on w2k and later platforms, the NetworkStream.Read
// method called in ReceiveTalk will generate an exception when
// the remote connection closes. We handle this case in our
// catch block below.
ReceiveTalk;
// On Win9x platforms, NetworkStream.Read returns 0 when
// the remote connection closes, prompting a graceful return
// from ReceiveTalk above. We will generate a Notification.End
// message here to handle the case and shut down the remaining
// WinTalk instance.
Notifications(Notification.End, "Remote connection has closed.");
}
else
{
Notifications(Notification.Error,
"Failed to Establish Socket, did you specify the correct port?");
}
}

catch(IOException e)
{
SocketException sockExcept = e.InnerException as SocketException;
if(sockExcept != null && 10054 == sockExcept.ErrorCode)
{
Notifications(Notification.End, "Remote connection has closed.");
}
else
{
if (Notifications != null)
Notifications(Notification.Error, "Socket Error:\n"+e.Message);
}

}
catch(Exception e)
{
Notifications(Notification.Error, "Socket Error:\n"+e.Message);
}

}
private void ReceiveTalk
{
char[] commandBuffer = new char[20];
char[] oneBuffer = new char[1];

int readMode = 1;
//int counter = 0;
StringBuilder text = new StringBuilder;
String tmp;
//, tmp1;
//tmp1 = "привет";
while(readMode != 0)
{


tmp = reader.ReadLine;

//tmp1 = PrintCPBytes(tmp, 1251);
if(tmp == "end")
{
readMode = 0;
continue;
}
Notifications(Notification.ReceivedRefresh, tmp);


}
}

evgen5555

О!
вместо

rsocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000);//debug


надо вставить

rsocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);//debug

как завещал

markmsk

Теперь исключение вылетает, даже если есть соединение с сервером ):

evgen5555

Какое именно исключение вылетает?

markmsk

>catch(IOException e)
> {
SocketException sockExcept = e.InnerException as SocketException;
Вот это.
Если увеличить время таймаута, то нормально - вылетает только при обрубании сервера, как и должно быть.

markmsk

Исключение вылетает при любом случае есть соединение или нет.
достаточно просто ничего не делать и подождать время таймаута.
Может нужно в отдельном потоке что-то отправлять на сервер и ждать по атймауту, или какой-нибудь другой вариант?

evgen5555

Маза, если не знаком с таким изобретением, как асинхронные сокеты, то про потоки советую забыть.
А
подождать время таймаута
действительно необходимо?

markmsk

если программа две секунды ничего не делает, то вылетает исключение. Это не дело.

evgen5555

Во-первых, если программа целых две(!) секунды ждет, то это уже не дело.
А во-вторых, в каком конкретно месте возникает исключение?

markmsk

>catch(IOException e)
Вот эта штука ловит исключение, которое появляется при подключении РесивТафмАута.
>Во-первых, если программа целых две(!) секунды ждет, то это уже не дело.
Дело в том что не ждёт а не взаимодейстьтвует с сервером, а такое вполне возможно, то есть если я сервер не отрубаю, а просто ничего не делаю, то вылетает исключение, такого быть не должно.

markmsk

А что думаешь по поводу вот этой вещи
LingerOption lingerOption = new LingerOption(true, 2);
rsocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lingerOption);

evgen5555

При чем тут Linger?

evgen5555

Короче, вешай на OnReceive обработчик, и все тут.

markmsk

>OnReceive
куда вешать?

markmsk

>rsocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 2000);//debug
Вот эту вещь вставляю в разные места кода, выскакивает исключение если 2 секунды замешкаться, так быть не должно.
Что за обработчик к ней ещё можно приделать?

evgen5555

Опс, улетел в другую степь.
Всё-таки непонятно, почему задержка блокирующим Receive и следующее за ним возникновение ошибки - неправильное поведение. Структура программы и протокол обмена ясны? Или это просто какой-то пример, взятый с потолка?

markmsk

Нет это пример конкретный и сразу же тестируемый на ходу так сказать объяснений.
>Всё-таки непонятно, почему задержка блокирующим Receive и следующее за ним возникновение ошибки - неправильное поведение.
Потому что непонятно почему исключение вызвано при обрыве сервера или просто прога ничего не делает.
Цель - нужно отследить те моменты когда теряется связь с сервером и принять меры.

markmsk

SocketOptionName.KeepAlive Field
Может эта штука лучше?

evgen5555

Цель - нужно отследить те моменты когда теряется связь с сервером и принять меры.
Ну так и цепляй эти действия к обработчику SocketException-а, в чем проблема?

markmsk

>Ну так и цепляй эти действия к обработчику SocketException-а, в чем проблема?
Непонятно как это сделать

evgen5555



...
catch(IOException e)
{
SocketException sockExcept = e.InnerException as SocketException;
MessageBox.Show("Работы ведуться. :cool: "); // Вот сюда нужно вставлять обработчик.
}
...

markmsk

Там уже есть обработчик.

evgen5555

Какой именно?

markmsk

Объясню что происходит, я вставил ReceiveTimeout 2000, после объявления rsockеt, обработчик исключения есть, сделан так же как ты предлагал. После запуска проги она сразу же (через 2сек) вываливается в это исключение, видимо ничего не пришло от сервера.

markmsk

SocketException sockExcept = e.InnerException as SocketException;
if(sockExcept != null && 10054 == sockExcept.ErrorCode)
{
Notifications(Notification.End, "Remote connection has closed.");
}
else
{
if (Notifications != null)
Notifications(Notification.Error, "Socket Error:\n"+e.Message);
}

evgen5555

После запуска проги она сразу же (через 2сек) вываливается в это исключение
После запуска или после вызова Receive?

markmsk

Скорее после вызова Recieve

evgen5555

Ну так если сервер не выполняет свои обязанности, то клиент в этом не виноват.

markmsk

Что можно сделать в этом случае?
Кстати нашёл в инете что с ресивомтаймаут часто бывает подобная проблема

evgen5555

Что можно сделать в этом случае?
Увеличивать время таймаута, разве непонятно?
Лично я проблемы никакой не вижу, код рабочий.

Marinavo_0507

у всех виденных мной сетевых приложений, от которых требуется надёжность, были какие-то специальные таймеры
независимо от того, блокирующие сокеты использовались, или нет
но они не под .net были, конечно

evgen5555

Ну вот, а во фреймворкских сокетах таймеры уже вкрученные с завода идут. Пользуйся, казалось бы, сколько влезет...

markmsk

Так увеличивал такая же хуйня

Marinavo_0507

Это ж далеко не мелкомягкое изобретение, SO_RCVTIMEO и SO_SNDTIMEO не вчера появились...
Вот только afaik не любят их, хз почему, наверное есть подводные камни.

otets-mihail

>вкрученные с завода идут.

markmsk

вот такое вот исключение выскакивает {"An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call" }

markmsk

Может есть что почитать на эту тему?

sergey_m

Это ж далеко не мелкомягкое изобретение, SO_RCVTIMEO и SO_SNDTIMEO не вчера появились...
Вот только afaik не любят их, хз почему, наверное есть подводные камни.
afaik, не любят их в Linux. Где еще не любят?

markmsk

Пробывал ещё вот такой вот вариант


Int32 timeout = (Int32)rsocket.GetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout);
// try to set what we received (we don't want to change the default value)
rsocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout);


В результате в одном случае получаю timeOut == -1 и исключение {"An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call" }
В другом случае timeOut == 0 и исключение не вылетает.
Оставить комментарий
Имя или ник:
Комментарий: