LINUX.ORG.RU

accept() ведёт себя по-разному в С и С++? Errno=17 File exists.

 


0

3

UPD: видимо, я сообщил мало информации, чтобы местные умники смогли вычислить проблему. Решил оставить сервер на C++ и не переделывать. Всем спасибо!

Здравствуйте. Переписываю программу с С++ на С. Всё скомпилировалось, но проблема выскочила там, где я не ожидал. Логика приложения не изменилась, accept() на блокирующем слушающем сокете, одинаковая инициализация; в С++ работает без вопросов, а в С возвращает -1 и устанавливает errno = 17 (File exists), то есть не то чтобы проскакивал с WOULDBLOCK, а именно ошибка выполнения функции. Дебаггер показывает, что iWaitingSocketDescriptor подаётся на вход верный.

Разница в коде вот такая:

void *MyClass::Accept_Thread ( void *vptr_args ) {
	int iNewConnection;
	do {
		iNewConnection = accept ( iWaitingSocketDescriptor, NULL, NULL );
}

static void *Accept_Thread ( void *vptr_args ) {
	int iNewConnection;
	do {
		iNewConnection = accept ( iWaitingSocketDescriptor, NULL, NULL );
}

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

Как я уже писал, инициализация в обоих случаях одна и та же:

  stWaitingSocket.sin_family = AF_INET;
  stWaitingSocket.sin_port = htons ( 1025 );
  stWaitingSocket.sin_addr.s_addr = htonl ( INADDR_ANY );
  iWaitingSocketDescriptor = socket ( AF_INET, SOCK_STREAM, 0 );
  iResult = bind ( iWaitingSocketDescriptor, ( struct sockaddr * ) &stWaitingSocket, sizeof ( stWaitingSocket ) );
  iResult = listen ( iWaitingSocketDescriptor, 1 );
... и проходит без ошибок.

Где-то ошибка, но, видимо, глаз замылился или, что скорее, из-за неопытности не знаю или просто не понимаю, что делаю не так. Подскажите пожалуйста?



Последнее исправление: cetjs2 (всего исправлений: 2)

Переписываю программу с С++ на С

Just for fun или есть какие-то причины для этого?

UVV ★★★★★
()
Ответ на: комментарий от UVV

Just for fun или есть какие-то причины для этого?

void *MyClass::Accept_Thread ( void *vptr_args ) {

Судя по всему, оно изначально было на С, по-другому объяснить это трудно.

anonymous
()
Ответ на: комментарий от anonymous

Ага, пздц, переписывает с Ц на Ц.

anonymous
()
Ответ на: комментарий от anonymous

Я вот даже в код не смотрел, а ты такое показываешь.. Срамота!

UVV ★★★★★
()

Заполнил stWaitingSocket нулями перед инициализацией полей?

Deleted
()
Ответ на: комментарий от hizel

Два последних комментатора - спасибо, я понял, о чём и один, и второй, сейчас вернулся, буду смотреть в эту сторону.

Ребятам с предыдущих комментариев - веселиться и смеяться, конечно, хорошо, но вот если бы объяснили, что там смешного, чтобы так не делать больше - было бы вообще замечательно.

shotakoe
() автор топика
Ответ на: комментарий от shotakoe

Я тебе задал вопрос, который ты проигнорировал.

Аноним резонно заметил, что void *MyClass::Accept_Thread ( void *vptr_args ) не С++, а С с классами.

UVV ★★★★★
()

возвращает -1 и устанавливает errno = 17 (File exists)

accept(2) не должна устанавливать такой errno.

Код, который проверяет возвращаемое accept(2) значение ты не привел, поэтому остается только ванговать о том, что у тебя там кривая проверка и использование мусорного значения.

edigaryev ★★★★★
()
Ответ на: комментарий от edigaryev

edigaryev, точно. Ещё раз проверил, подпрограмма вывода логов использовало errno, а он, видимо, от файла к файлу не глобальный, в подпрограмме всё время принимал значение 17. Стал передавать в неё errno в качестве параметра и получил, что Errno=9: Bad file descriptor.

iWaitingSocketDescriptor в accept() передаётся тот же по значению, что и создаётся в socket().

shotakoe
() автор топика
Ответ на: комментарий от Deleted

@ mironov_ivan Спасибо, Иван, буду смотреть внимательнее.

shotakoe
() автор топика
Ответ на: комментарий от slackwarrior

у некоторых

«Consider bringing your OS back to your vendor for a refund» (c)

edigaryev ★★★★★
()

Даже не знаю, что делать. Всё несколько раз осмотрел, слушающий сокет с правильным адресом в памяти, с правильным значением по этому адресу в accept() передаётся. Разница лишь в том, что доступ к переменной iWaitingSocketDescriptor в случае компилирования g++ осуществляется как к статической переменной-членом класса из статической функции-метода класса, а в случае gcc из статической функции, в файле которой переменная объявлена как extern. Никого это не натолкнёт на мысль, которую захотите высказать?

P. S. stWaitingSocket обнулял, SO_REUSEADDR - для случаев, в которых bind сообщает already in use, насколько я помню, тут вроде не в том дело? :/

shotakoe
() автор топика
Ответ на: комментарий от shotakoe

Никого это не натолкнёт на мысль, которую захотите высказать?

«Макаронный код» - единственное что приходит на ум.

asaw ★★★★★
()
Ответ на: комментарий от eao197

Да, но к этому сокету единственное обращение вот этот accept, только из этого одного потока, клиент ещё не запущен.

shotakoe
() автор топика
Ответ на: комментарий от shotakoe

Неаккуратное обращение с разделяемыми между несколькими потоками данными может привести к самым причудливым последствиям. Не видя полного кода вряд ли можно вам помочь.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ясно, спасибо за такое желание. Там дело в том, что изменился только способ доступа к переменной. Остальное осталось аналогичным, отсюда был и вопрос - не разнятся ли accept при компиляции gcc и от g++. До меня уже дошло, что нет, баг в чём-то другом, но я уже бросил: «C с классами» версия работает, буду её дорабатывать.

shotakoe
() автор топика
Ответ на: комментарий от shotakoe

Ну так иди по шагам! Поменяй статическое поле на глобальную переменную, потом поменяй статический метод на функцию и т. п. Поймешь на каком шаге ломается - можно будет гадать более лучше.

anonymous
()
Ответ на: комментарий от UVV

Расскажи о нужности использования void* в c++, пжлста. Особенно в коде ТСа.

При чем тут нужность? Или void* был выпилен из c++?

andreyu ★★★★★
()
Ответ на: комментарий от UVV

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

shotakoe
() автор топика
Ответ на: комментарий от andreyu

Нет, конечно. goto ведь тоже остался.

UVV ★★★★★
()
Ответ на: комментарий от Deleted

Нашёл. Вы были правы, в одном месте был участок кода, который отвечал за выключение сокетов по тайм-ауту и в нём отсутствовала необходимая проверка одного условия, в следствии чего закрывался слушающий сокет из-за смещения по памяти. Разница была в том, что при компиляции разными компиляторами память распределяется по-разному. Оттого даже то, что в g++ версии тот же участок кода так же некорректно работает, он не производил тех действий, которые к этому приводили. В-общем, ваш комментарий оказался самым точным, спасибо :)

shotakoe
() автор топика
Ответ на: комментарий от shotakoe

... в следствии чего закрывался слушающий сокет из-за смещения по памяти.

Я бы сказал, что твои сказки про курицу с яйцом тут не помогут. Случай запущенный. Нужно срочно начинать лечение. Начни с Effective C++, Scott Meyers.

UVV ★★★★★
()
Ответ на: комментарий от UVV

Чувак, мы читали твои темы про C++. Лучше не надо.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.