|
|
  |
rawprint lwip |
|
|
|
May 14 2013, 07:39
|
Группа: Новичок
Сообщений: 3
Регистрация: 3-05-06
Пользователь №: 16 735

|
Имеется следующая железка: интерфейсная плата принтера на Cortex-M3 + lwip. Поверх lwip реализован простой raw-print сервер, который слушает порт 9100 и отсылает принятые данные в принтер. Все работает хорошо до тех пор, пока в одно и то же время не поступают две и более задач с планировщиков печати (спулеров) разных хостов. Суть вопроса: необходимо средствами lwip разграничить доступ к принтеру, то есть обеспечить режим "занят" в момент обработки текущей задачи. На данный момент есть эксперементальный код, который, собственно практически не работает... Идея в том чтобы запомнить ip для текущей задачи (rawprint_remote_ip) и пока соединение не будет закрыто отправлять все новые задачи через tcp_poll в ожидание. Инициализация: Код void rawprintd_init(void) { struct tcp_pcb *pcb;
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprintd_init\n"));
pcb = tcp_new(); tcp_bind(pcb, IP_ADDR_ANY, 9100); pcb = tcp_listen(pcb); tcp_accept(pcb, rawprint_accept);
} Подтвержение: CODE static err_t rawprint_accept(void *arg, struct tcp_pcb *pcb, err_t err) { struct rawprint_state *rs;
LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(err);
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_accept 0x%08x\n", pcb));
/* Allocate memory for the structure that holds the state of the connection. */ rs = (struct rawprint_state *)mem_malloc(sizeof(struct rawprint_state));
if (rs == NULL) { LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_accept: Out of memory\n")); return ERR_MEM; }
/* Initialize the structure. */ rs->retries = 0;
/* Tell TCP that this is the structure we wish to be passed for our callbacks. */ tcp_arg(pcb, rs);
/* Tell TCP that we wish to be informed of incoming data by a call to the http_recv() function. */ if (pcb->local_port == 9100) { if (rawprint_remote_ip == 0) { tcp_recv(pcb, rawprint_recv);
tcp_err(pcb, conn_err); rawprint_remote_ip = pcb->remote_ip.addr; rawprint_send_status(pcb, false);
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_remote_ip %08x\n", rawprint_remote_ip)); } else { LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("go poll\n")); tcp_poll(pcb, rawprint_poll, 1); } }
return ERR_OK; } Прием данных: CODE static err_t rawprint_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { tBoolean bSendStatus = false; struct rawprint_state *rs;
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_recv 0x%08x port %d\n", pcb, (unsigned int)pcb->local_port));
rs = arg;
if ((err == ERR_OK) && (p != NULL) && rs) {
/* Inform TCP that we have taken the data. */ tcp_recved(pcb, p->tot_len);
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_recv length %d bytes\n", p->tot_len));
if (p->len) { const unsigned char *pucData = p->payload; switch(pcb->local_port) { case 9100: if (!((pucData[0] == 0x00) && (p->len == 1))) rawprint_printer_write(p); if ((memcmp(g_pucReadPort, pucData, sizeof(g_pucReadPort)) == 0) && (p->len == sizeof(g_pucReadPort))) bSendStatus = true; break; } } pbuf_free(p); }
if (((err == ERR_OK) && (p == NULL)) || (bSendStatus)) { rawprint_send_status(pcb, true); } return ERR_OK; } Опрос: CODE static err_t rawprint_poll(void *arg, struct tcp_pcb *pcb) { struct rawprint_state *rs;
rs = arg;
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_poll 0x%08x port %d\n", pcb, pcb->local_port));
/* printf("Polll\n");*/ if ((rs == NULL) && (pcb->state == ESTABLISHED)) { /* printf("Null, close\n");*/ tcp_abort(pcb); return ERR_ABRT; } else { if (rawprint_remote_ip == 0) { tcp_recv(pcb, rawprint_recv);
tcp_err(pcb, conn_err);
rawprint_remote_ip = pcb->remote_ip.addr; rawprint_send_status(pcb, false);
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_remote_ip %08x\n", rawprint_remote_ip)); }
}
return ERR_OK; } Закрытие соединения: CODE /*-----------------------------------------------------------------------------------*/ static void close_conn(struct tcp_pcb *pcb, struct rawprint_state *rs) { err_t err; LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("Closing connection 0x%08x\n", pcb));
tcp_arg(pcb, NULL); tcp_sent(pcb, NULL); tcp_recv(pcb, NULL); if(rs) { mem_free(rs); }
if (pcb->remote_ip.addr == rawprint_remote_ip){ rawprint_remote_ip = 0; } err = tcp_close(pcb); if(err != ERR_OK) { LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("Error %d closing 0x%08x\n", err, pcb)); } } Я не совсем уверен в правильности вызова функции tcp_poll без tcp_recv в случае, когда задача уже запущена (rawprint_remote_ip != 0). Есть у кого-нибудь идеи как реализовать правильно без буферизации, если это, конечно возможно?
Сообщение отредактировал IgorKossak - May 14 2013, 07:54
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
May 14 2013, 08:17
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
if (((err == ERR_OK) && (p == NULL)) || (bSendStatus)) { rawprint_send_status(pcb, true); }
вот это несколько странно.
Если со стороны компьютера закрыть коннект, условие выполниться, нулевой буфер данных придет. А вы на это попробуете послать обратно статус, а его уже могут и не ждать...
В калбеках функции есть аргументы которые можно задать при установке соединения, я бы в эти аргументы ставил бы порядковый номер задачи-конекта. Чтобы каждый коннект при вызове ресивед имел свой номер. И еще глобально бы хранил очередь номеров, когда начинается печать, запоминается номер который начал печать, его приемы обрабатываете и пихаете в принтер, все остальные пропускаете. Не подтверждайте им что вы приняли от них данные. Как то так...
|
|
|
|
|
May 15 2013, 06:53
|
Группа: Новичок
Сообщений: 3
Регистрация: 3-05-06
Пользователь №: 16 735

|
Цитата(Golikov A. @ May 14 2013, 12:17)  if (((err == ERR_OK) && (p == NULL)) || (bSendStatus)) { rawprint_send_status(pcb, true); }
вот это несколько странно.
Если со стороны компьютера закрыть коннект, условие выполниться, нулевой буфер данных придет. А вы на это попробуете послать обратно статус, а его уже могут и не ждать... С этим как раз все ок. Спулер желает знать статус принтера до выполнения задачи (при коннекте) и после ее завершения. Цитата(Golikov A. @ May 14 2013, 12:17)  В калбеках функции есть аргументы которые можно задать при установке соединения, я бы в эти аргументы ставил бы порядковый номер задачи-конекта. Чтобы каждый коннект при вызове ресивед имел свой номер. И еще глобально бы хранил очередь номеров, когда начинается печать, запоминается номер который начал печать, его приемы обрабатываете и пихаете в принтер, все остальные пропускаете. Не подтверждайте им что вы приняли от них данные. Как то так... Изначально я пытался сделать так, но без очереди и уникального номера задачи, так как с этим нормально справляется ip, так как он и является уникальным для любой входящей задачи. Например вот так: Подтверждение. CODE static err_t rawprint_accept(void *arg, struct tcp_pcb *pcb, err_t err) { struct rawprint_state *rs;
LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(err);
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_accept 0x%08x\n", pcb));
/* Allocate memory for the structure that holds the state of the connection. */ rs = (struct rawprint_state *)mem_malloc(sizeof(struct rawprint_state));
if (rs == NULL) { LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_accept: Out of memory\n")); return ERR_MEM; }
/* Initialize the structure. */ rs->retries = 0;
/* Tell TCP that this is the structure we wish to be passed for our callbacks. */ tcp_arg(pcb, rs);
tcp_recv(pcb, rawprint_recv);
tcp_err(pcb, conn_err);
/* Tell TCP that we wish to be informed of incoming data by a call to the http_recv() function. */ if (pcb->local_port == 9100) { if (rawprint_remote_ip == 0) { rawprint_remote_ip = pcb->remote_ip.addr; rawprint_send_status(pcb, false);
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_remote_ip %08x\n", rawprint_remote_ip)); } }
return ERR_OK; } То есть мы принимаем все соединения и только одно получает право на выполнение. Соблюдение FIFO нас не очень интересует. Тогда Прием данных: CODE static err_t rawprint_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { tBoolean bSendStatus = false; struct rawprint_state *rs;
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_recv 0x%08x port %d\n", pcb, (unsigned int)pcb->local_port));
rs = arg;
if ((err == ERR_OK) && (p != NULL) && rs) {
if (pcb->local_port == 9100) { if (pcb->remote_ip.addr == rawprint_remote_ip) { /* Inform TCP that we have taken the data. */ tcp_recved(pcb, p->tot_len); } else if (rawprint_remote_ip == 0) { /* Inform TCP that we have taken the data. */ tcp_recved(pcb, p->tot_len); rawprint_remote_ip = pcb->remote_ip.addr;
LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_remote_ip 0x%08x\n", rawprint_remote_ip)); } else { LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("discard***\n", rawprint_remote_ip)); return ERR_OK; } } LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("rawprint_recv length %d bytes\n", p->tot_len));
if (p->len) { const unsigned char *pucData = p->payload; switch(pcb->local_port) { case 9100: if (!((pucData[0] == 0x00) && (p->len == 1))) rawprint_printer_write(p); if ((memcmp(g_pucReadPort, pucData, sizeof(g_pucReadPort)) == 0) && (p->len == sizeof(g_pucReadPort))) bSendStatus = true; break; } } pbuf_free(p); }
if (((err == ERR_OK) && (p == NULL)) || (bSendStatus)) { rawprint_send_status(pcb, true); } return ERR_OK; } То есть мы подтверждаем прием данных только тогда, когда задача является активной (pcb->remote_ip.addr == rawprint_remote_ip) либо ни одна задача таковой не является (rawprint_remote_ip == 0). Не работает. (((((
Сообщение отредактировал IgorKossak - May 15 2013, 13:35
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
May 15 2013, 08:12
|
Группа: Новичок
Сообщений: 3
Регистрация: 3-05-06
Пользователь №: 16 735

|
Цитата(Golikov A. @ May 15 2013, 11:27)  почему не работает? а где рав принт ремоте сбрасывается в 0? по идее по дисконекту это должно было бы произойти. Так и есть.. см в первом посте: Закрытие соединения: CODE /*-----------------------------------------------------------------------------------*/ static void close_conn(struct tcp_pcb *pcb, struct rawprint_state *rs) { err_t err; LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("Closing connection 0x%08x\n", pcb));
tcp_arg(pcb, NULL); tcp_sent(pcb, NULL); tcp_recv(pcb, NULL); if(rs) { mem_free(rs); }
if (pcb->remote_ip.addr == rawprint_remote_ip){ rawprint_remote_ip = 0; } err = tcp_close(pcb); if(err != ERR_OK) { LWIP_DEBUGF(RAW_PRINTD_DEBUG, ("Error %d closing 0x%08x\n", err, pcb)); } } Если ip закрываемого соединения ip текущей задачи (pcb->remote_ip.addr == rawprint_remote_ip). Цитата(Golikov A. @ May 15 2013, 11:27)  а вы учитываете что p - имеет еще поле тотал лен и ссылку на следущий p Да. Учитываю. Вот код функции записи в принтер. Код static void rawprint_printer_write(struct pbuf *psBuf) { while(psBuf != NULL) { int iCount = 0; const unsigned char *pucData = psBuf->payload; while (iCount < psBuf->len) { iCount += PrinterWriteBuffer(&pucData[iCount], psBuf->len - iCount); } psBuf = psBuf->next; } } Цитата(Golikov A. @ May 15 2013, 11:27)  вызывая функцию ресивед вы говорите сколько данных из всего окна вы обработали, но те окна что вы пропустили не обработав, могут получить и 2 пакет данных и третий, и перейдя к ним надо бы все данные обработать... Вот это место не очень понял.
Сообщение отредактировал IgorKossak - May 15 2013, 13:36
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|