Re[3]: сокеты (проблема с приемом/отправкой)
От: alexku Россия  
Дата: 19.11.03 16:26
Оценка: 3 (1)
Извини, что долго не отвечал. Не было возможности.
Ты уже разобрался с селектом? Если да, то можешь не читать .

Я посмотрел твой код.
Что тебе нужно делать:
1. Использовать селект при каждом чтении и записи в сокет
2. Проверять возвращаемое функциями recv и send значение ВСЕГДА.

Селект останавливает выполнение процесса/потока до того момента, когда во входном буфере сокета появятся данные, готовые к употреблению или сокет, в который ты собираешься гнать данные, будет готов их принять. Иначе ты впустую тратишь время процессора на бесполезный цикл чтения/записи. Селект можно использовать не только с сокетами, а в принципе с любыми файловыми дескрипторами (по крайней мере с stdin точно можно, про остальное не уверен).
Я не буду повторять man, укажу только главное.

int select(номер_дескриптора_с_максимальным_значением + 1,/*!!!Не количество дескрипторов!!!*/
           набор_дескрипторов_для_чтения,/*0 если читать ничего не надо*/
           набор_дескрипторов_для_записи,/*0 если записывать ничего не надо*/
           набор_дескрипторов_для_исключений,/*ставь смело в 0*/
           время_ожидания)


В примере устанавливается на ожидание только один сокет и только на чтение или только на запись, но ты можешь установить их столько, сколько надо и на оба действия. Например, stdin можешь поставить на чтение, а сокет — на запись. Появились данные в stdin — прочитал, готов сокет — записал.

int socketReady(int s, int rw, int sec, int usec) {//rw = 0 - чтение, rw = 1 - запись
  struct timeval tv;
  fd_set rfds;
  fd_set wfds;
  fd_set *cfds;
  int res;

  FD_ZERO(&rfds); //читай man select
  FD_ZERO(&wfds);

  tv.tv_sec = sec;
  tv.tv_usec = usec;

  if(!rw) cfds = &rfds; //ожидаем сокет на чтение
  else    cfds = &wfds; //ожидаем сокет на запись

  FD_SET(s, cfds); //читай man select
  
  //обрати внимание на первый параметр.
  //!!!селект сбрасывает значения rfds и wfds, поэтому их надо устанавливать
  //перед каждым вызовом селект.
  res = select(s + 1, &rfds, &wfds, 0, &tv);
  if(res == -1) {
    // error
    return ERROR;
  } else if(!res) {
    // socket not ready, timeout expired
    return END_TIMEOUT;
  }
  //проверяем, готов ли нужный сокет
  if(FD_ISSET(s, cfds)) //читай man select
    return READY;
  else return ERROR;
}


и дальше

wile(1) {
  if(READY == (result = socketReady(s, 0, 10, 0))) { //читаем
    currRead = recv(s, readBuff, readBuffLen, 0);
    if(currRead = -1) {
      //ошибка, проверяем errno и вываливаемся из цикла или нет
    } else if(currRead == 0) {
      //тоже ненормально, значит сокет на том конце закрыт, вываливаемся из цикла
    } else {
      //работаем с тем, что получили,
      //количество полученного равно currRead
    }
  } else {
    //проверяем, что вернулось и т. д.
  }
}


и запись


wile(1) {
  if(READY == (result = socketReady(s, 1, 10, 0))) { //пишем
    sent = send(s, writeBuff, len, 0);
    if(sent = -1) {
      //ошибка, проверяем errno и вываливаемся из цикла или нет
    } else if(sent < len) {
      //записалось не всё, нужно будет дописывать то, что осталось в буфере
    } else {
      //готовим следующую порцию или заканчиваем
    }
  } else {
    //проверяем, что вернулось и т. д.
    //и вываливаемся из цикла, если надо
  }
}


И ещё несколько замечаний.
Нет никакого признака того, что из сокета прочитаны все данные. И то, что прочитано меньше, чем размер буфера, не говорит ни о чём. Нужно самостоятельно как-то уведомлять получателя о количестве отправленных данных или о том, что достигнут конец данных (какая-нибудь волшебная завершающая последовательность).
Если из сокета прочитано 0 байт, это значит то, он закрыт на другом конце, а не то, что в него отправлено 0 байт.
Сокет может быть закрыт на запись и открыт на чтение и наоборот. Смотри man 2 shutdown.
Если время ожидания вышло, это говорит только о том, что в читающем сокете до сих пор ничего нет или из пишущего сокета ничего не отправлено получателю (может он там просто забыл читать из сокета). Здесь надо самому принимать решение, что делать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.