& quot; Соединение было разорвано & quot; ошибка с UDT (протокол передачи данных на основе UDP)

Я программирую игру в реальном времени, в которой мне нужен надежный UDP, поэтому я решил работать с UDT (протокол передачи данных на основе UDP — http://sourceforge.net/projects/udt/).

Клиенты (в браузерах) отправляют сообщения в реальном времени на мой сервер через CGI-скрипты. Проблема в том, что некоторые сообщения теряются, и я не знаю почему, потому что сервер говорит, что он успешно отправил все сообщения соответствующим клиентам, но иногда клиент не получает сообщение.

В моем файле отладки я обнаружил, что когда клиент не получает сообщение, его скрипт говорит:

error in recv();
recv: Connection was broken.

Я хотел бы получить некоторую помощь о том, как сервер узнает, получил ли клиент свое сообщение; я должен отправить NACK или что-то со стороны клиента? Я думал, что UDT должен сделать это для меня. Может кто-нибудь прояснить эту ситуацию?

Ниже приведены соответствующие разделы коммуникационной части моего кода с некоторыми комментариями:

соответствующий код сервера:

//...

void send_msg_in(player cur, char* xml){
/*this function stores the current message, xml, in a queue if xml!=NULL, and sends the 1st message of the queue to the client*/
/*this function is called when the player connects with the entering xml=NULL to get the 1st message of the queue,
or with xml!=NULL when a new message arrives: in this case the message is stored in the queue, and then the message will be sent in the appropriate time, i.e. the messages are ordered.*/
char* msg_ptr=NULL;

if (xml!=NULL){         //add the message to a queue (FIFO), the cur.xml_msgs
msg_ptr=(char*) calloc(strlen(xml)+1, sizeof(char));
strcpy(msg_ptr, xml);
(*(cur.xml_msgs)).push(msg_ptr);
}                       //get the 1st message of the queue
if (!(*(cur.xml_msgs)).empty()){
xml=(*(cur.xml_msgs)).front();
}

if (cur.get_udt_socket_in()!=NULL){
UDTSOCKET cur_udt = *(cur.get_udt_socket_in());
//      cout << "send_msg_in(), cur_udt: " << cur_udt << endl;

//send the "xml", i.e. the 1st message of the queue...
if (UDT::ERROR == UDT::send(cur_udt, xml, strlen(xml)+1, 0)){
UDT::close(cur_udt);
cur.set_udt_socket_in(NULL);
}
else{    //if no error this else is reached
cout << "TO client:\n" << xml << "\n";    /*if there is no error,
i.e. on success, the server prints the message that was sent.*/
//  / \
// /_!_\
/*the problem is that
the messages that are lost don't appear on the client side,
but they appear here on the server! */
if (((string) xml).find("<ack.>")==string::npos){
UDT::close(cur_udt);
cur.set_udt_socket_in(NULL);          //close the socket
}
(*(cur.xml_msgs)).pop();
}
}
}

//...

код клиента

//...
#define MSGBUFSIZE 1024
char msgbuf[MSGBUFSIZE];
UDTSOCKET client;
ofstream myfile;

//...

main(int argc, char *argv[]){
//...

// connect to the server, implict bind
if (UDT::ERROR == UDT::connect(client, (sockaddr*)&serv_addr, sizeof(serv_addr))){
cout << "error in connect();" << endl;
return 0;
}

myfile.open("./log.txt", ios::app);

send(xml);
char* cur_xml;

do{
cur_xml = receive();                //wait for an ACK or a new message...
myfile << cur_xml << endl << endl;  //  / \
/* /_!_\ the lost messages don't appear on the website
neither on this log file.*/
} while (((string) cur_xml).find("<ack.>")!=string::npos);
cout << cur_xml << endl;

myfile.close();
UDT::close(client);
return 0;
}char* receive(){
if (UDT::ERROR == UDT::recv(client, msgbuf, MSGBUFSIZE, 0)){
//  / \
/* /_!_\ when a message is not well received
this code is usually reached, and an error is printed.*/
cout << "error in recv();" << endl;
myfile << "error in recv();" << endl;
myfile << "recv: " << UDT::getlasterror().getErrorMessage() << endl << endl;
return 0;
}
return msgbuf;
}void* send(string xml){
if (UDT::ERROR == UDT::send(client, xml.c_str(), strlen(xml.c_str())+1, 0)){
cout << "error in send();" << endl;
myfile << "error in send();" << endl;
myfile << "send: " << UDT::getlasterror().getErrorMessage() << endl << endl;
return 0;
}
}

Спасибо за любую помощь!

PS. Я пытался увеличить время задержки на close (), после нахождения ссылки http://udt.sourceforge.net/udt4/doc/opt.htm, добавив следующее к коду сервера:

    struct linger l;
l.l_onoff  = 1;
l.l_linger = ...;  //a huge value in seconds...
UDT::setsockopt(*udt_socket_ptr, 0, UDT_LINGER, &l, sizeof(l));

но проблема все та же …

PPS. другие части коммуникации на стороне сервера: (примечание: мне кажется, что они не так актуальны)

main(int argc, char *argv[]){
char msgbuf[MSGBUFSIZE];

UDTSOCKET serv = UDT::socket(AF_INET, SOCK_STREAM, 0);

sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(my_addr.sin_zero), '\0', sizeof(my_addr.sin_zero));

if (UDT::ERROR == UDT::bind(serv, (sockaddr*)&my_addr, sizeof(my_addr))){
cout << "error in bind();";
return 0;
}

UDT::listen(serv, 1);

int namelen;
sockaddr_in their_addr;

while (true){
UDTSOCKET recver = UDT::accept(serv, (sockaddr*)&their_addr, &namelen);
if (UDT::ERROR == UDT::recv(recver, msgbuf, MSGBUFSIZE, 0)){
//this recv() function is called only once for each aqccept(), because the clients call CGI scripts via a browser, they need to call a new CGI script with a new UDT socket for each request (this in in agreement to the clients' code presented before).
cout << "error in recv();" << endl;
}

char* player_xml = (char*) &msgbuf;
cur_result = process_request((char*) &msgbuf, &recver, verbose);    //ACK
}
}struct result process_request(char* xml, UDTSOCKET* udt_socket_ptr, bool verbose){
//parse the XML...
//...

player* cur_ptr = get_player(me);   //searches in a vector of player, according to the string "me" of the XML parsing.

UDTSOCKET* udt_ptr = (UDTSOCKET*) calloc(1, sizeof(UDTSOCKET));
memcpy(udt_ptr, udt_socket_ptr, sizeof(UDTSOCKET));

if (cur_ptr==NULL){
//register the player:
player* this_player = (player*) calloc(1, sizeof(player));
//...
}
}
else if (strcmp(request_type.c_str(), "info_waitformsg")==0){
if (udt_ptr!=NULL){
cur_ptr->set_udt_socket_in(udt_ptr);

if (!(*(cur_ptr->xml_msgs)).empty()){
send_msg_in(*cur_ptr, NULL, true);
}
}
}
else{           //messages that get instant response from the server.
if (udt_ptr!=NULL){
cur_ptr->set_udt_socket_out(udt_ptr);
}
if (strcmp(request_type.c_str(), "info_chat")==0){
info_chat cur_info;
to_object(&cur_info, me, request_type, msg_ptr);    //convert the XML string values to a struct
process_chat_msg(cur_info, xml);
}
/*  else if (...){  //other types of messages...
}*/
}
}void process_chat_msg(info_chat cur_info, char* xml_in){
player* player_ptr=get_player(cur_info.me);

if (player_ptr){
int i=search_in_matches(matches, cur_info.match_ID);
if (i>=0){
match* cur_match=matches[i];
vector<player*> players_in = cur_match->followers;
int n=players_in.size();
for (int i=0; i<n; i++){
if (players_in[i]!=msg_owner){
send_msg_in(*(players_in[i]), xml, flag);
}
}
}
}
}

1

Решение

Глядя на исходный код UDT в http://sourceforge.net/p/udt/git/ci/master/tree/udt4/src/core.cpp, сообщение об ошибке «Соединение было разорвано» выдается, когда один из логических флагов m_bBroken или же m_bClosing является true и нет данных в буфере приема.

Эти флаги устанавливаются только в нескольких случаях:

  • В разделах кода с пометкой «не должно происходить; атака или ошибка» (маловероятно)
  • В преднамеренных действиях по закрытию или закрытию (в вашем коде этого не происходит)
  • По истечении таймера, который проверяет активность сверстников (вероятный виновник)

В этом исходном файле в строке 2593 написано:

     // Connection is broken.
// UDT does not signal any information about this instead of to stop quietly.
// Application will detect this when it calls any UDT methods next time.
//
m_bClosing = true;
m_bBroken = true;

// ...[code omitted]...

// app can call any UDT API to learn the connection_broken error

Глядя на send() звоните, я нигде не вижу, что он ожидает ACK или NAK от партнера, прежде чем вернуться, поэтому я не думаю, что успешное возвращение из send() на стороне сервера свидетельствует об успешном получении сообщения клиентом.

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

1

Другие решения

UDP не является протоколом с гарантированной передачей. Хост отправит сообщение, но если получатель его не получит или если он не получен должным образом, ошибка не возникнет. Поэтому он обычно используется в приложениях, требующих скорости, а не в идеальной доставке, таких как игры. TCP гарантирует доставку, потому что он требует, чтобы сначала было установлено соединение, и каждое сообщение подтверждается клиентом.

Я бы посоветовал вам подумать о том, действительно ли вам нужно гарантированное получение этих данных, и, если вы это сделаете, подумать об использовании TCP.

0

По вопросам рекламы [email protected]