C sockets在Windows上,随机收到recv错误,但errno为0?

huangapple go评论75阅读模式
英文:

C sockets on windows, randomly getting recv errors but errno is at 0?

问题

It appears that you have provided code for a client-server communication using sockets in a Windows environment. If you have any specific questions or need assistance with a particular part of this code, please feel free to ask.

英文:

I have a client and a server using sockets on windows. I start the server before the client, wait for it to hang and then start the client. Doing so, the call to recv() will randomly (half of the time) return -1 and when I print errno it is at 0 (No Error)... The other half of the time it will receive the message and print it normally then close... Is there something wrong with this code ?

client.c

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <errno.h>

#define BUFFER_LEN 200

int main()
{
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2,0), &WSAData);
    
    SOCKET sock;
	SOCKADDR_IN address;
	// address.sin_addr.s_addr = inet_addr("127.0.0.1");
	InetPton(AF_INET, "127.0.0.1", &address.sin_addr.s_addr);
	address.sin_family = AF_INET;
	address.sin_port = htons(8888);
	
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		printf("Erreur lors de la création du socket. %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	if ((connect(sock, (SOCKADDR *)&address, sizeof(address))) == -1) {
		sprintf("Erreur lors de la connexion au socket. %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	char *msg = "Coucou\n";
	printf("Envoi de coucou\n");
	int bytesSent;

	if ((bytesSent = send(sock, msg, strlen(msg), 0)) == -1) {
		sprintf("Erreur lors l'envoi du message. %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	printf("Bytes envoyés : %d\n", bytesSent);

    WSACleanup();
    
    return 0;
}

server.c

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define BACKLOG 3 // taille de la file d'attente
#define BUFFER_LEN 200 // taille du buffer de lecture

int main()
{
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2,0), &WSAData);
    
	SOCKET sockfd;
	SOCKADDR_IN address;
	// address.sin_addr.s_addr = inet_addr("127.0.0.1");
	address.sin_addr.s_addr = htonl(INADDR_ANY);  
	address.sin_family = AF_INET;
	address.sin_port = htons(8888); // host to network short (conversion de host byte order en network byte order)
	
	printf("Démarrage du serveur\n");

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		printf("Erreur lors de la création du socket. %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	if ((bind(sockfd, (SOCKADDR *)&address, sizeof(address))) == -1) {
		printf("Erreur lors de l'ouverture du socket. %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	if ((listen(sockfd, BACKLOG)) == -1) {
		printf("Erreur lors de l'écoute du socket. %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	SOCKET clientSocket;
	SOCKADDR_IN clientAddress;
	int addrLen = sizeof(clientAddress);
	if ((clientSocket = accept(sockfd, (struct sockaddr *) &clientAddress, &addrLen)) == -1) {
		printf("Erreur lors de l'acceptation de la connexion. %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	// Convertion de l'IP en texte
	char ip[INET_ADDRSTRLEN];
	inet_ntop(AF_INET, &(clientAddress.sin_addr), ip, INET_ADDRSTRLEN);
	printf("Connexion de %s:%i\n", ip, clientAddress.sin_port);

	char buffer[BUFFER_LEN];
	int len = recv(clientSocket, buffer, BUFFER_LEN, 0);

	if (len == -1 && errno != EAGAIN) {
		printf("Erreur lors de la réception du message. %s (%d)\n", strerror(errno), errno);
		exit(EXIT_FAILURE);
	} else if (len == 0) {
		printf("Le client s'est déconnecté (extrémité de la socket fermée)\n");
		exit(EXIT_FAILURE);
	} else if (len > 0) {
		printf("Message reçu : %s\n", buffer);
	}

	closesocket(clientSocket);
    closesocket(sockfd);

    WSACleanup();

    return 0;
}

I also should mention that I compile with gcc main.c -lws2_32 -g -Wall

答案1

得分: 5

请注意,在客户端调用send()后返回时,这并不意味着任何数据已发送到服务器。这只是表示数据已排队等待发送。

然后,您的客户端立即调用WSACleanup()并退出。似乎这会立即销毁您的套接字,而在发送任何数据之前。

您应该在发送数据后和调用WSACleanup()之间添加closesocket(sock)。这将允许套接字以有序的方式关闭,包括刷新任何已排队等待发送的数据。

如@Hans所指出,读取errno不是获取套接字错误的正确方法,您应该使用WSAGetLastError()

英文:

The first thing to note is that when the call to send() in your client returns, this does not mean that any data has been sent to your server. It just means the data has been queued up to be sent.

Your client then immediately calls WSACleanup() and exits. It would appear that this just instantly destroys your socket, before it has had a chance to send anything.

You should add closesocket(sock) between sending your data, and calling WSACleanup(). This will allow the socket to be closed in an orderly manner, including flushing any data that has been queued up.

As pointed out by @Hans, reading errno is not the correct way to get a socket error, you should use WSAGetLastError().

huangapple
  • 本文由 发表于 2023年6月9日 04:21:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76435449.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定