好吧,伙计们,我已经连续工作了两天,寻找错误并寻找去年的解决方案和其他软件,但发现
我的问题是,我需要编写一个相当简单的服务器客户端应用程序,在其中接收响应并回复它们。我编写了较小的程序来演示我的问题。
在您提示发送和接收缺乏完整性之前,要检查的发送和接收不为< 0。我知道我必须检查这一点。
common.h 只是所有输入和静态变量的 header 。
// guard block:
#ifndef COMMON_H
#define COMMON_H
// default hostname and port:
#define DEFAULT_HOST "localhost"
#define DEFAULT_PORT "1280"
#include <stdarg.h>
#include <ctype.h>
// IO, C standard library, POSIX API, data types:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
// Sockets, TCP, ... :
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
// Assertions, errors, signals:
#include <assert.h>
#include <errno.h>
#include <signal.h>
static struct addrinfo *ai = NULL; // stores address information
static int sockfd = -1; // socket file descriptor
static int connfd = -1; // connection file descriptor
void terminate(int sig);
void init_signal_handler(void);
void free_ressources(void);
void error(char* format, ...);
void terminate(int sig) {
error("Caught signal %d. Terminating...\n",sig);
}
void init_signal_handler(void) {
struct sigaction sa;
sa.sa_handler = terminate;
if (-1 == sigemptyset(&(sa.sa_mask))) {
error("sigemptyset()");
}
if (-1 == sigaction(SIGINT, &sa, NULL)) {
error("sigaction()");
}
if (-1 == sigaction(SIGTERM, &sa, NULL)) {
error("sigaction()");
}
}
void error(char *format, ...) {
va_list arg;
va_start (arg, format);
(void) vfprintf (stderr, format, arg);
va_end (arg);
free_ressources();
exit(EXIT_FAILURE);
}
void free_ressources(void) {
(void) printf("Freeing all ressources...\n");
if(sockfd != -1) close(sockfd);
if(connfd != -1) close(connfd);
freeaddrinfo(ai);
}
#endif // COMMON_H
这是客户端
#include "common.h"
static char *port = DEFAULT_PORT;
static char *host = DEFAULT_HOST;
int main(int argc, char *argv[]) {
int s;
struct addrinfo hints, *rp;
memset(&ai, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
s = getaddrinfo(host, port, &hints, &ai);
if(s != 0) error("Couldn't get address info...\n");
for (rp=ai; rp != NULL; rp=rp->ai_next) {
sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if(sockfd == -1) continue;
if((connfd = connect(sockfd, rp->ai_addr, rp->ai_addrlen)) != -1) break;
close(sockfd);
}
if(rp == NULL) error("Couldn't connect...\n");
uint16_t msg = 0xF0F0;
uint8_t msg_r = 0x00;
for(int i = 0; i < 10; ++i) {
(void) fprintf(stdout, "Sending message %#X\n", msg);
send(sockfd, &msg, 2, MSG_CONFIRM);
recv(connfd, &msg_r, 1, MSG_CONFIRM);
(void) fprintf(stdout, "Recieved from Server %#X\n", msg_r);
msg+=1;
}
free_ressources();
}
这是服务器
#include "common.h"
static char *port = DEFAULT_PORT;
int main(int argc, char *argv[]) {
struct addrinfo hints;
memset(&ai, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int res = getaddrinfo(NULL, port, &hints, &ai);
if(res != 0) error("Failed to get addr info: %s\n", gai_strerror(res));
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(sockfd == -1) error("Couldn't create a socket\n");
int val = 1;
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val);
if(res == -1) error("Socket options couldn't be set\n");
res = bind(sockfd, ai->ai_addr, ai->ai_addrlen);
if(res == -1) error("Socket binding failed\n");
res = listen(sockfd, 1);
if(res == -1) error("Listener setup failed\n");
connfd = accept(sockfd, NULL, NULL);
if(connfd == -1) error("Connect failed\n");
uint8_t msg_s = 0x00;
uint16_t msg = 0x0000;
for(int i = 0; i < 10; ++i) {
msg_s+=1;
recv(connfd, &msg, 2, MSG_CONFIRM);
(void) fprintf(stdout, "Recieved from Client %#X\n", msg);
(void) fprintf(stdout, "Sending %#X\n", msg_s);
send(sockfd, &msg_s, 1, MSG_CONFIRM);
}
free_ressources();
}
好吧,我对此有几个问题。
如果我朝一个方向执行服务器客户端应用程序,例如仅客户端发送且仅服务器接收,则一切都会一如既往地顺利且符合预期。然而,当我像这里一样将其混合时,事情就会发生变化。我知道 TCP 是一种没有固定“包”数量的流协议(protocol),但我也可以理解如何在最多只需要 2 个字节的情况下实现它。我可能会尝试一次只接收 1 个字节,如果需要 2 个字节,则在循环中执行此操作,但为什么呢?为什么在单车道模式下一切都很好,而在混合模式下却一团糟?
我尝试使用 recv 并相应地发送到其手册页,因此将它们与套接字一起使用。然而我不知道为什么,但每当我尝试将recv与sock_fd一起使用时,它就不起作用,但另一边的conn_fd却可以。但我到处都看到我显然应该使用 sock_fd。
这是我混合接收和发送时的输出
服务器的输出
Recieved from Client 0XF0F0
Sending 0X1
客户端的输出
Sending message 0XF0F0
Recieved from Server 0
Sending message 0XF0F1
Recieved from Server 0
Sending message 0XF0F2
Recieved from Server 0
Sending message 0XF0F3
Recieved from Server 0
Sending message 0XF0F4
Recieved from Server 0
Sending message 0XF0F5
Recieved from Server 0
Sending message 0XF0F6
Recieved from Server 0
Sending message 0XF0F7
Recieved from Server 0
Sending message 0XF0F8
Recieved from Server 0
Sending message 0XF0F9
Recieved from Server 0
Freeing all ressources...
总之,我知道这可能是一个愚蠢而简单的问题,但为什么会这样呢?为什么服务器在发送第一条消息时就崩溃了,而客户端却将消息扔进了虚空。
感谢您的提前答复,我真的需要解决这个问题。
最佳答案
您有一些小问题和一些大问题:
小:
- 您不会影响
hints
中的ai_protocol
,但当hints
为hints
时,getaddrinfo()
需要它不NULL
- 您使用
connect()
的返回值作为文件描述符,但它是一个错误代码号。您必须在客户端中使用socket()
返回的文件描述符,或在服务器中使用accept()
返回的文件描述符。 - 在服务器中使用
send()
时使用了错误的文件描述符。您不应该在处理SOCK_STREAM
套接字类型中的连接的文件描述符上发送数据。 getaddrinfo()
返回一个列表,您应该迭代它,但您假设总是有一个结果,但它们可能是零个或多个。hints
已初始化,您可以使用memset()
调用未定义的行为,因为sizeof(struct addrinfo) > sizeof(struct addrinfo *)
所以当你在ai
上写零时,你就出界了(全局...全局无处不在)
大:
- 您毫无理由地使用全局
- 您一行声明多个变量。
- 您毫无理由地使用全局
- 您没有验证所有系统调用错误!
- 您毫无理由地使用全局
// server and client
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_PASSIVE,
.ai_protocol = IPPROTO_TCP,
};
// server
if (send(connfd, &msg_s, 1, MSG_CONFIRM) == -1) {
// error
}
// client
if (recv(sockfd, &msg_r, 1, MSG_CONFIRM) == -1) {
// error
}
关于c - 发送和接收不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49866247/