Я пытаюсь написать базовый c эхо-сервер TCP в C на macOS Catalina. Я следую за man-страницами моей среды и этими веб-страницами: Реализация TCP Server-Client в C - GeeksforGeeks и Пример сервера и клиента с C сокетами на Linux - BinaryTides .
По какой-то причине мой клиент (я полагаю) блокирует свой первый вызов recv
. Я новичок в программировании сокетов, поэтому я не знаю, почему это может быть. Почему мой клиент не распечатывает ответ, который он должен получить от сервера?
сервер. c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define CLOSE(fildes) if (close(fildes) == -1) { \
perror("call to `close` failed"); \
exit(EX_OSERR); \
}
#define MAXCONNS 4
#define MSGLEN 1023
int
main(int argc, char **argv)
{
long port;
int passive_fildes, active_fildes;
struct sockaddr_in address;
ssize_t length;
char message[MSGLEN];
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(EX_USAGE);
}
if ((port = strtol(argv[1], NULL, 10)) > UINT16_MAX || port < 1024) {
fputs("invalid port argument\n", stderr);
exit(EX_USAGE);
}
if ((passive_fildes = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("failed to create socket");
exit(EX_OSERR);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(port);
if (bind(passive_fildes, (struct sockaddr *)&address,
sizeof(struct sockaddr_in)) == -1) {
perror("failed to bind socket");
CLOSE(passive_fildes);
exit(EX_OSERR);
}
if (listen(passive_fildes, MAXCONNS) == -1) {
perror("failed to listen for connections");
CLOSE(passive_fildes);
exit(EX_OSERR);
}
/* Accept connections indefinitely. */
for (;;) {
if ((active_fildes = accept(passive_fildes, NULL, NULL)) ==
-1) {
perror("failed to accept connection");
/* Should we exit here? */
continue;
}
/* Respond to requests until client disconnects. */
while ((length = recv(active_fildes, message, MSGLEN, 0)) !=
0) {
if (length == -1) {
perror("failed to receive request");
continue;
}
if (message[length-1] != '\n') {
fprintf(stderr, "request was over %d ", MSGLEN);
fprintf(stderr, "characters in length\n");
/* We break the client if we don't respond. */
break;
}
if (send(active_fildes, message, (size_t)length, 0) ==
-1) {
perror("failed to send response");
/* We break the client if we don't respond. */
break;
}
}
CLOSE(active_fildes);
}
CLOSE(passive_fildes);
return 0;
}
клиент. c
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define CLOSE(fildes) if (close(fildes) == -1) { \
perror("call to `close` failed"); \
exit(EX_OSERR); \
}
#define MSGLEN 1023
int
main(int argc, char **argv)
{
struct sockaddr_in address;
long port;
int fildes;
char message[MSGLEN+1];
ssize_t length;
if (argc != 3) {
fprintf(stderr, "usage: %s <address> <port>\n", argv[0]);
exit(EX_USAGE);
}
if (inet_aton(argv[1], &address.sin_addr) == 0) {
perror("invalid address argument\n");
exit(EX_USAGE);
}
if ((port = strtol(argv[2], NULL, 10)) > UINT16_MAX || port < 1024) {
fputs("invalid port argument\n", stderr);
exit(EX_USAGE);
}
if ((fildes = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("failed to create socket");
exit(EX_OSERR);
}
address.sin_family = AF_INET;
address.sin_port = htons(port);
if (connect(fildes, (struct sockaddr *)&address,
sizeof(struct sockaddr_in)) == -1) {
perror("failed to connect to server");
CLOSE(fildes);
exit(EX_NOHOST);
}
while (fgets(message, MSGLEN+1, stdin) != NULL) {
/* Check whether the entire request was read. */
if (message[strlen(message)-1] != '\n') {
fprintf(stderr, "request was over %d ", MSGLEN);
fprintf(stderr, "characters in length\n");
/* Gobble the rest of the request. */
while (fgetc(stdin) != '\n');
continue;
}
if (send(fildes, message, strlen(message), 0) == -1) {
perror("failed to send request");
continue;
}
/* Don't clobber the EOS! */
if ((length = recv(fildes, message, MSGLEN, 0)) == -1) {
perror("failed to receive response");
/* Not reading response is an error condition. */
CLOSE(fildes);
exit(EX_OSERR);
}
if (length == 0) {
fputs("server unexpectedly closed connection", stderr);
CLOSE(fildes);
exit(EX_PROTOCOL);
}
/* Check whether the entire response was read. */
if (message[length-1] != '\n') {
fprintf(stderr, "response was over %d ", MSGLEN);
fprintf(stderr, "characters in length\n");
/* Gobble the rest of the response. */
do if (recv(fildes, message, 1, 0) == -1) {
perror("failed to gobble extra characters");
/* This is an error condition. */
CLOSE(fildes);
exit(EX_OSERR);
} while (message[0] != '\n');
/* Can still print partial response. */
}
/* Validate the response with an EOS. */
message[length] = '\0';
if (fputs(message, stdout) == -1) {
perror("failed to print response out");
continue;
}
}
if (!feof(stdin)) {
perror("failed to read request in");
CLOSE(fildes);
exit(EX_OSERR);
}
CLOSE(fildes);
return 0;
}
Серверная консоль
$ ./server 8080
entered main loop
accepted connection
received request: hello
sent response: hello
Клиентская консоль
$ ./client 127.0.0.1 8080
connected to server
hello
После того, как клиент распечатал сообщение о соединении, я ввел «привет»; однако клиент никогда не выводит ответ от сервера. Обратите внимание на оператор отладочной печати в строке 73 в клиенте. c никогда не выполняется. Это показывает, что вызов recv
не завершен правильно. Тем не менее, оператор отладочной печати в строке 87 на сервере выполняется. c, что показывает, что сервер завершает свой вызов send
.
Я очень тщательно пытался написать правильный код C, поэтому, если у любого из вас есть комментарии о безопасности или стиле, пожалуйста, дайте им!