我正在尝试在macOS Catalina上的C语言中编写基本的TCP回显服务器。我正在关注环境的手册页和以下网页:TCP Server-Client implementation in C - GeeksforGeeks和Server and client example with C sockets on Linux - BinaryTides。
出于某种原因,我的客户(我相信)在第一次致电recv
时被阻止。我是套接字编程的新手,所以我不知道为什么会这样。为什么我的客户端不打印出应该从服务器得到的响应?
#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 ERR_BIGREQ "request was over %d characters in length\n"
#define MAXCONN 4
#define BUFLEN 1024
int
main(int argc, char **argv)
{
long port;
int passive_fildes, active_fildes;
struct sockaddr_in address;
ssize_t length;
char message[BUFLEN];
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\n", stderr);
exit(EX_USAGE);
}
if ((passive_fildes = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("call to `socket` failed");
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("call to `bind` failed");
CLOSE(passive_fildes);
exit(EX_OSERR);
}
if (listen(passive_fildes, MAXCONN) == -1) {
perror("call to `listen` failed");
CLOSE(passive_fildes);
exit(EX_OSERR);
}
/* Accept connections indefinitely. */
for (;;) {
/* DEBUG */ fputs("entered main loop\n", stderr);
if ((active_fildes = accept(passive_fildes, NULL, NULL)) ==
-1) {
perror("call to `accept` failed");
/* Should we exit here? */
continue;
}
/* DEBUG */ fputs("accepted connection\n", stderr);
/* Respond to requests until client disconnects. */
while ((length = recv(active_fildes, message, BUFLEN, 0)) !=
0) {
if (length == -1) {
perror("call to `recv` failed");
continue;
}
if (message[length-1] != '\n') {
fprintf(stderr, ERR_BIGREQ, BUFLEN);
/* We break the client if we don't respond. */
CLOSE(active_fildes);
break;
}
/* DEBUG */ fprintf(stderr, "received request: %s", message);
if (send(active_fildes, message, (size_t)length, 0) ==
-1) {
perror("call to `send` failed");
/* We break the client if we don't respond. */
CLOSE(active_fildes);
break;
}
/* DEBUG */ fprintf(stderr, "sent response: %s", message);
}
CLOSE(active_fildes);
}
CLOSE(passive_fildes);
return 0;
}
#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 ERR_BIGREQ "request is over %d characters in length\n"
#define ERR_BIGRES "response is over %d characters in length\n"
#define BUFFER 1024
int
main(int argc, char **argv)
{
struct sockaddr_in address;
long port;
int fildes;
char message[BUFFER];
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");
exit(EX_USAGE);
}
if ((port = strtol(argv[2], NULL, 10)) > UINT16_MAX || port < 1024) {
fputs("invalid port\n", stderr);
exit(EX_USAGE);
}
if ((fildes = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("call to `socket` failed");
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("call to `connect` failed");
CLOSE(fildes);
exit(EX_NOHOST);
}
/* DEBUG */ fputs("connected to server\n", stderr);
while (fgets(message, BUFFER, stdin) != NULL) {
/* Check whether the entire request was read. */
if (message[strlen(message)-1] != '\n') {
fprintf(stderr, ERR_BIGREQ, BUFFER-2);
continue;
}
if (send(fildes, message, strlen(message), 0) == -1) {
perror("call to `send` failed");
continue;
}
/* Don't clobber the EOS! */
if ((length = recv(fildes, message, BUFFER-1, 0)) == -1) {
perror("call to `recv` failed");
/* Not reading response is an error condition. */
CLOSE(fildes);
exit(EX_OSERR);
}
/* DEBUG */ fputs("got a response\n", stderr);
/* Validate the response with an EOS. */
message[length] = '\0';
/* Check whether the entire response was read. */
if (message[length-1] != '\n') {
fprintf(stderr, ERR_BIGRES, BUFFER-1);
/* Gobble the rest of the response. */
do if (recv(fildes, message, 1, 0) == -1) {
perror("call to `recv` failed");
/* This is an error condition. */
CLOSE(fildes);
exit(EX_OSERR);
} while (message[0] != '\n');
/* Can still print partial response. */
}
fputs(message, stdout);
}
if (!feof(stdin)) {
perror("call to `fgets` failed");
CLOSE(fildes);
exit(EX_OSERR);
}
CLOSE(fildes);
return 0;
}
entered main loop
accepted connection
received request: hello
sent response: hello
connected to server
客户端打印出连接消息后,我输入“ hello”;但是,客户端从不输出服务器的响应。请注意,永远不会执行client.c中第73行的调试打印语句。这表明recv
调用未正确完成。但是,将执行server.c中第87行的调试打印语句,该语句显示服务器已完成其send
调用。
我非常努力地编写了正确的C代码,因此,如果您对安全性或样式有任何意见,请给他们!
我无法复制您的问题。
这里是我的Catalina系统的笔录:
server$ ./server 11111
entered main loop
accepted connection
received request: hello from client
sent response: hello from client
entered main loop
^C
server$
client$ ./client 127.0.0.1 11111
connected to server
hello from client
got a response
hello from client
^C
client$