我有一个服务器和一个客户端(代码发布在下面)
我正在使用客户端程序中的 SO_RCVTIMEO 超时选项测试 setsockopt 功能,它似乎正在编译中。
过程是:服务器绑定到套接字以进行读取 - 客户端向其发送消息 - 服务器显示消息并将其重新发送给发送它的客户端 - 最后,客户端显示从服务器收到的消息
因此,要在X毫秒数之后检查超时是否实际失败(仅当客户端等待从服务器发回消息时才使用超时),我在服务器之前插入了一个Sleep(miliseconds)函数。将消息重新发送到客户端,以平衡客户端和服务器之间的这种延迟。
因此,当使用Sleep(0)进行测试并且在0处超时时,一切都完美同步,但是如果我将值提升到两端的10 ms,结果应该与0相同,但实际上,一切都是按计划工作,除非客户端等待服务器的响应,它超时,尽管两端的延迟完全相同。
作为最后一个例子:如果服务器上的睡眠是10毫秒,并且客户端上的超时是11,甚至15以留下更多空间,那么会发生什么,服务器接收消息,等待10毫秒,重新发送它,客户端是等待另外5ms完成15ms,实际上超时。更糟糕的是,服务器在完成整个过程后恢复了它的初始等待响应循环,不知何故给我一个错误的recvfrom。
请原谅这篇庞大的文字,但如果不遗漏一些细节,就很难总结所有这些内容。
Client.c(忽略广播,不使用)
#include <winsock.h>
#include <stdio.h>
#define SERV_HOST_ADDR "127.0.0.1"
#define SERV_UDP_PORT 6000
#define PORT_MAX 7000
#define PORT_MIN 1000
#define NUM_IT 10
#define BUFFERSIZE 4096
void Abort(char *msg);
/*________________________________ main _______________________________________
*/
int main(int argc, char *argv[])
{
SOCKET sockfd;
int msg_len, iResult, nbytes, addrlength, local_port;
struct sockaddr_in serv_addr, local;
char buffer[BUFFERSIZE];
WSADATA wsaData;
//broadcast
int broadcast, broadcast_loop;
//timeout
DWORD timeout;// = TIMEOUT;
/*========================= TESTA A SINTAXE =========================*/
if (argc != 9){
fprintf(stderr, "(%d argumentos) Sintaxe: %s -msg \"msg\" -ip ip -port port -timeout microsegundos\n (ordem arbitraria)\n", argc, argv[0]);
exit(EXIT_FAILURE);
}
//tratar os argumentos//
int pos_msg, pos_port, pos_ip, pos_timeout, i;
for (i = 0; i < argc; i++)
{
if (strcmp(argv[i], "-msg") == 0)
pos_msg = i + 1;
if (strcmp(argv[i], "-ip") == 0)
pos_ip = i + 1;
if (strcmp(argv[i], "-port") == 0)
pos_port = i + 1;
if (strcmp(argv[i], "-timeout") == 0)
pos_timeout = i + 1;
}
timeout = atol((char*)(LPCTSTR)argv[pos_timeout]); //converter timeout STRING para DWORD
if (strcmp(argv[pos_ip], "255.255.255.255") == 0)
{
broadcast_loop = 1;
broadcast = 1;
}
else
{
broadcast_loop = 0;
broadcast = 0;
}
/*=============== INICIA OS WINSOCKS ==============*/
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
getchar();
exit(1);
}
/*=============== CRIA SOCKET PARA ENVIO/RECEPCAO DE DATAGRAMAS ==============*/
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
if (sockfd == INVALID_SOCKET)
Abort("Impossibilidade de criar socket");
/*================ CONFIGURA O SOCKET PARA TIMEOUT DE RECEPCAO ==============*/
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
//this call is what allows broadcast packets to be sent:
if (broadcast_loop == 1)
{
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof broadcast) == -1)
{
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
}
/*================= PREENCHE ENDERECO DO SERVIDOR ====================*/
memset((char*)&serv_addr, 0, sizeof(serv_addr)); /*Coloca a zero todos os bytes*/
serv_addr.sin_family = AF_INET; /*Address Family: Internet*/
//serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR); /*IP no formato "dotted decimal" => 32 bits*/
serv_addr.sin_addr.s_addr = inet_addr(argv[pos_ip]);
//serv_addr.sin_port = htons(SERV_UDP_PORT); /*Host TO Netowork Short*/
serv_addr.sin_port = htons(atoi(argv[pos_port]));
/*====================== ENVIA MENSAGEM AO SERVIDOR ==================*/
msg_len = strlen(argv[pos_msg]);
if (broadcast_loop == 1)
{
//loop sem esperar pelo reenvio
for (int i = 0; i < NUM_IT; i++)
{
for (int pt = PORT_MIN; pt <= PORT_MAX; pt++)
{
serv_addr.sin_port = htons(pt);
if (sendto(sockfd, argv[pos_msg], msg_len, 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
Abort("SO nao conseguiu aceitar o datagram");
printf(" -Enviada mensagem %d (port: %d)\n", i, pt);
//ir buscar a porta gerada automaticamente pare este socket e mostrá-la
addrlength = sizeof(local);
if (getsockname(sockfd, (struct sockaddr *)&local, &addrlength) == 0 && local.sin_family == AF_INET && addrlength == sizeof(local))
{
local_port = ntohs(local.sin_port);
}
printf("<CLI1>Mensagem enviada (port: %d) ... aguardando pelo seu reenvio.\n", local_port);
}
//////// ESPERAR QUE MESMA MENSAGEM SEJA REENVIADA PELO SERVIDOR //////
////nbytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);
////memset((char*)&serv_addr, 0, sizeof(serv_addr));
//addrlength = sizeof(serv_addr);
//nbytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&serv_addr, &addrlength);
//if (nbytes == SOCKET_ERROR)
// Abort("Erro na recepcao da mensagem");
//buffer[nbytes] = '\0'; /*Termina a cadeia de caracteres recebidos com '\0'*/
//printf("\n<CLI1>Mensagem recebida {%s}\n", buffer);
//if (strcmp(inet_ntoa(serv_addr.sin_addr), SERV_HOST_ADDR) == 0 && ntohs(serv_addr.sin_port) == SERV_UDP_PORT)
// printf(" -Mensagem enviada pelo servidor -> %s:%d\n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
//else
// printf(" -Mensagem NAO enviada pelo servidor -> %s:%d\n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
}
}
else
{
if (sendto(sockfd, argv[pos_msg], msg_len, 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
Abort("SO nao conseguiu aceitar o datagram");
//ir buscar a porta gerada automaticamente pare este socket e mostrá-la
addrlength = sizeof(local);
if (getsockname(sockfd, (struct sockaddr *)&local, &addrlength) == 0 && local.sin_family == AF_INET && addrlength == sizeof(local))
{
local_port = ntohs(local.sin_port);
}
printf("<CLI1>Mensagem enviada (port: %d) ... aguardando pelo seu reenvio (timeout).\n", local_port);
////// ESPERAR QUE MESMA MENSAGEM SEJA REENVIADA PELO SERVIDOR //////
//nbytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);
//memset((char*)&serv_addr, 0, sizeof(serv_addr));
addrlength = sizeof(serv_addr);
nbytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&serv_addr, &addrlength);
if (nbytes == SOCKET_ERROR)
if (WSAGetLastError() != WSAETIMEDOUT)
Abort("Erro na recepcao da mensagem");
else
Abort("Timeout de recepcao");
buffer[nbytes] = '\0'; /*Termina a cadeia de caracteres recebidos com '\0'*/
printf("\n<CLI1>Mensagem recebida {%s}\n", buffer);
if (strcmp(inet_ntoa(serv_addr.sin_addr), SERV_HOST_ADDR) == 0 && ntohs(serv_addr.sin_port) == SERV_UDP_PORT)
printf(" -Mensagem enviada pelo servidor -> %s:%d\n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
else
printf(" -Mensagem NAO enviada pelo servidor -> %s:%d\n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
}
/*========================= FECHA O SOCKET ===========================*/
closesocket(sockfd);
printf("\n");
system("Pause");
exit(EXIT_SUCCESS);
}
/*________________________________ Abort________________________________________
Mostra uma mensagem de erro e o código associado ao ultimo erro com Winsocks.
Termina a aplicacao com "exit status" a 1 (constante EXIT_FAILURE)
________________________________________________________________________________*/
void Abort(char *msg)
{
fprintf(stderr, "<CLI1>Erro fatal: <%s> (%d)\n", msg, WSAGetLastError());
exit(EXIT_FAILURE);
}
和Server.c
#include <stdio.h>
#include <winsock.h>
#include <Windows.h> //Sleep()
#define SERV_UDP_PORT 6000
#define BUFFERSIZE 4096
void Abort(char *msg);
/*________________________________ main ________________________________________
*/
int main(int argc, char *argv[])
{
SOCKET sockfd;
int iResult, nbytes, msg_len, len;
struct sockaddr_in serv_addr, cli_addr;
char buffer[BUFFERSIZE];
WSADATA wsaData;
/*========================= TESTA A SINTAXE =========================*/
if (argc != 5){
fprintf(stderr, "(%d argumentos) Sintaxe: %s -port port -delay delay\n", argc, argv[0]);
exit(EXIT_FAILURE);
}
//tratar os argumentos//
int pos_port, pos_delay, i;
for (i = 0; i < argc; i++)
{
if (strcmp(argv[i], "-port") == 0)
pos_port = i + 1;
if (strcmp(argv[i], "-delay") == 0)
pos_delay = i + 1;
}
/*=============== INICIA OS WINSOCKS ==============*/
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
getchar();
exit(1);
}
/*============ CRIA O SOCKET PARA RECEPCAO/ENVIO DE DATAGRAMAS UDP ============*/
if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
Abort("Impossibilidade de abrir socket");
/*=============== ASSOCIA O SOCKET AO ENDERECO DE ESCUTA ===============*/
/*Define que pretende receber datagramas vindos de qualquer interface de
rede, no porto pretendido*/
memset((char*)&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; /*Address Family: Internet*/
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*Host TO Network Long*/
serv_addr.sin_port = htons(atoi(argv[pos_port])); /*Host TO Network Short*/
/*Associa o socket ao porto pretendido*/
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
Abort("Impossibilidade de registar-se para escuta");
/*================ PASSA A ATENDER CLIENTES INTERACTIVAMENTE =============*/
while (1){
fprintf(stderr, "<SER1>Esperando datagram...\n");
len = sizeof(cli_addr);
//recieve from, tem agora de saber de quem recebeu, para reenviar
nbytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&cli_addr, &len);
if (nbytes == SOCKET_ERROR)
Abort("Erro na recepcao de datagrams");
buffer[nbytes] = '\0'; /*Termina a cadeia de caracteres recebidos com '\0'*/
printf("<SER1>Mensagem recebida {%s} de %s:%d\n", buffer, inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
////// REENVIAR A MENSAGEM AO CLIENTE //////
msg_len = strlen(buffer);
Sleep(atoi(argv[pos_delay])); //sleep de 'delay' milisegundos
if (sendto(sockfd, buffer, msg_len, 0, (struct sockaddr*)&cli_addr, sizeof(cli_addr)) == SOCKET_ERROR)
Abort("SO nao conseguiu aceitar o datagram");
printf("<SER1>Mensagem reenviada.\n\n");
}
system("Pause");
}
/*________________________________ Abort________________________________________
Mostra uma mensagem de erro e o código associado ao ultimo erro com Winsocks.
Termina a aplicacao com "exit status" a 1 (constante EXIT_FAILURE)
________________________________________________________________________________*/
void Abort(char *msg)
{
fprintf(stderr, "<SER1>Erro fatal: <%s> (%d)\n", msg, WSAGetLastError());
printf("\n");
system("Pause");
exit(EXIT_FAILURE);
}
如果有人能够理解所有这些帖子并提供一些有用的答案,我将非常感激。