C套接字问题——随机客户端地址和端口号
C socket problems - random client address and port number
我有一个非常简单的练习要做:制作一个套接字,当客户端连接时,服务器会显示其信息(地址和端口号)。
我写了这个:
client.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
// check arguments
if (argc != 3)
{
fprintf(stderr, "Erreur argument\n");
exit(EXIT_FAILURE);
}
char *nom_machine = argv[1];
int port = atoi(argv[2]);
int sock;
struct sockaddr_in addr;
// création socket
sock = socket(AF_INET, SOCK_STREAM, 0);
// remplissage struct de l'adresse
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // code les octets dans l'ordre réseau
inet_aton(nom_machine, &addr.sin_addr);
// connnexion à 1.2.3.4:4242
connect(sock, (struct sockaddr *)&addr, sizeof addr);
// display informations
fprintf (stderr,
"---------\nAdresse: %s\nPort: %hd\n---------\n",
inet_ntoa(addr.sin_addr),
ntohs(addr.sin_port));
// envoie message
int n = 7;
int r = write(sock, &n, sizeof(int));
if (r != sizeof(int)) {
fprintf(stderr, "Echec de l'envoi de n\n");
exit(EXIT_FAILURE);
}
// arrêt
close(sock);
exit(EXIT_SUCCESS);
}
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Erreur argument\n");
exit(EXIT_FAILURE);
}
int port = atoi(argv[1]);
int sock;
struct sockaddr_in addr;
// création socket
sock = socket(AF_INET, SOCK_STREAM, 0);
// remplissage struct de l'adresse
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // code les octets dans l'ordre réseau
addr.sin_addr.s_addr = INADDR_ANY;
// création connnexion
bind(sock, (struct sockaddr *)&addr, sizeof addr);
// écoute
listen(sock, 2);
// attend une connexion entrante
struct sockaddr_in client_addr;
socklen_t size;
int lect = accept(sock, (struct sockaddr *)&client_addr, &size); // (2) structure entring connection
// display client informations
fprintf (stderr,
"---------\nAdresse: %s\nPort: %d\n---------\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
// lecture message
int n;
int r = read(lect, &n, sizeof(int));
if (r <= 0) {
fprintf(stderr, "Le serveur n'a pas recu le message\n");
close(sock);
exit(EXIT_FAILURE);
}
// Affichage
fprintf(stderr, "%d\n", n);
// arrêt
close(sock);
exit(EXIT_SUCCESS);
}
不幸的是,当我测试我的代码时,服务器给出的客户端地址和端口号似乎是完全随机的(除了有时它给出正确的地址,但我从未看到正确的端口号)。
这是一个例子:
客户端控制台
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ gcc -g -Wall -Wextra -Og -o client client.c
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./client 127.0.0.1 3333
---------
Adresse: 127.0.0.1
Port: 3333
---------
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./client 127.0.0.1 3333
---------
Adresse: 127.0.0.1
Port: 3333
---------
服务器控制台
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ gcc -g -Wall -Wextra -Og -o serveur serveur.c
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./serveur 3333
---------
Adresse: 6.127.0.0
Port: 16578
---------
Le serveur n'a pas recu le message
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./serveur 3333
---------
Adresse: 127.0.0.1
Port: 55098
---------
7
如果有人可以帮助找出我的错误。谢谢:)
您在 accept(2)
联机帮助页中遗漏了这一点:
The addrlen argument is a value-result argument: the caller must
initialize it to contain the size (in bytes) of the structure pointed
to by addr; on return it will contain the actual size of the peer
address.
这意味着,你必须做
socklen_t size = sizeof client_addr;
否则,如果 size
中的值(不确定)太小,returned 地址将被截断,这意味着内容可能保持未初始化(完全或部分)。这会导致您观察到奇怪的值。
另一个问题是,当端口最近被使用(TIME_WAIT 状态)时,bind()
可能会失败并显示 地址已被使用 。在这种情况下,端口从某个实现定义的范围自动绑定。为避免这种情况,您可以在 bind()
:
之前设置选项 SO_REUSEADDR
int one = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
此外,非常建议检查所有 return 值是否有错误;你会注意到 bind()
失败了。
我有一个非常简单的练习要做:制作一个套接字,当客户端连接时,服务器会显示其信息(地址和端口号)。
我写了这个:
client.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
// check arguments
if (argc != 3)
{
fprintf(stderr, "Erreur argument\n");
exit(EXIT_FAILURE);
}
char *nom_machine = argv[1];
int port = atoi(argv[2]);
int sock;
struct sockaddr_in addr;
// création socket
sock = socket(AF_INET, SOCK_STREAM, 0);
// remplissage struct de l'adresse
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // code les octets dans l'ordre réseau
inet_aton(nom_machine, &addr.sin_addr);
// connnexion à 1.2.3.4:4242
connect(sock, (struct sockaddr *)&addr, sizeof addr);
// display informations
fprintf (stderr,
"---------\nAdresse: %s\nPort: %hd\n---------\n",
inet_ntoa(addr.sin_addr),
ntohs(addr.sin_port));
// envoie message
int n = 7;
int r = write(sock, &n, sizeof(int));
if (r != sizeof(int)) {
fprintf(stderr, "Echec de l'envoi de n\n");
exit(EXIT_FAILURE);
}
// arrêt
close(sock);
exit(EXIT_SUCCESS);
}
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Erreur argument\n");
exit(EXIT_FAILURE);
}
int port = atoi(argv[1]);
int sock;
struct sockaddr_in addr;
// création socket
sock = socket(AF_INET, SOCK_STREAM, 0);
// remplissage struct de l'adresse
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // code les octets dans l'ordre réseau
addr.sin_addr.s_addr = INADDR_ANY;
// création connnexion
bind(sock, (struct sockaddr *)&addr, sizeof addr);
// écoute
listen(sock, 2);
// attend une connexion entrante
struct sockaddr_in client_addr;
socklen_t size;
int lect = accept(sock, (struct sockaddr *)&client_addr, &size); // (2) structure entring connection
// display client informations
fprintf (stderr,
"---------\nAdresse: %s\nPort: %d\n---------\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
// lecture message
int n;
int r = read(lect, &n, sizeof(int));
if (r <= 0) {
fprintf(stderr, "Le serveur n'a pas recu le message\n");
close(sock);
exit(EXIT_FAILURE);
}
// Affichage
fprintf(stderr, "%d\n", n);
// arrêt
close(sock);
exit(EXIT_SUCCESS);
}
不幸的是,当我测试我的代码时,服务器给出的客户端地址和端口号似乎是完全随机的(除了有时它给出正确的地址,但我从未看到正确的端口号)。
这是一个例子:
客户端控制台
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ gcc -g -Wall -Wextra -Og -o client client.c
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./client 127.0.0.1 3333
---------
Adresse: 127.0.0.1
Port: 3333
---------
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./client 127.0.0.1 3333
---------
Adresse: 127.0.0.1
Port: 3333
---------
服务器控制台
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ gcc -g -Wall -Wextra -Og -o serveur serveur.c
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./serveur 3333
---------
Adresse: 6.127.0.0
Port: 16578
---------
Le serveur n'a pas recu le message
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./serveur 3333
---------
Adresse: 127.0.0.1
Port: 55098
---------
7
如果有人可以帮助找出我的错误。谢谢:)
您在 accept(2)
联机帮助页中遗漏了这一点:
The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.
这意味着,你必须做
socklen_t size = sizeof client_addr;
否则,如果 size
中的值(不确定)太小,returned 地址将被截断,这意味着内容可能保持未初始化(完全或部分)。这会导致您观察到奇怪的值。
另一个问题是,当端口最近被使用(TIME_WAIT 状态)时,bind()
可能会失败并显示 地址已被使用 。在这种情况下,端口从某个实现定义的范围自动绑定。为避免这种情况,您可以在 bind()
:
SO_REUSEADDR
int one = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
此外,非常建议检查所有 return 值是否有错误;你会注意到 bind()
失败了。