我的讲师给了我们他使用TCP在C中实现的Daytime Protocol,我们必须将其扩展到UDP。它应该只接受UDP或使用cmd flags -u / -t的TCP。到目前为止,我已经完成了所有操作,但sendto()函数返回
“服务器已启动,正在侦听0.0.0.0:13...从127.0.0.1接收消息:35585:
来自127.0.0.1:35585的传入连接成功。函数sending_datetime()第319行出错:发送到()时出错:非套接字上的套接字操作“我已经尝试了所有方法,但仍然无法修复它,请帮助我,谢谢你:)(我是一个初学者)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <getopt.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <arpa/inet.h>
#define TCP 1
#define UDP 2
#define PROTO TCP
#define PORT 13
#define BACKLOG 256
#define MSGLENGTH 27
#define XSTR(s) STR(s)
#define STR(s) #s
#define ERROR(str) (fprintf(stderr, "Error in function %s() line %d: ", __FUNCTION__, __LINE__),\
perror(str), kill(0, SIGTERM), exit(EXIT_FAILURE))
#define LOG(...) (printf(__VA_ARGS__), fflush(stdout))
volatile sig_atomic_t active = 1;
/*
* struct srvcfg is used to manage all necessary settings for a daytime service
*/
struct srvcfg {
int proto;
unsigned short port;
struct in_addr server_ip;
struct sockaddr_in server_addr;
int sockfd;
};
/*
* Declaration of functions
*/
void exiting(int sig);
void set_signal_handler(void);
void print_usage(void);
void parse_arguments(int argc, char *argv[], struct srvcfg *cfg);
void start_server(struct srvcfg *cfg);
int create_socket(int type, struct sockaddr_in *addr);
int sending_datetime(int fd, int port);
void destroy_server(struct srvcfg *cfg);
int main(int argc, char *argv[]) {
struct srvcfg *cfg;
struct sockaddr_in client;
int client_len = sizeof(struct sockaddr_in);
int fd;
if ((cfg = malloc(sizeof(struct srvcfg))) == NULL)
ERROR("Error allocating memory");
set_signal_handler();
parse_arguments(argc, argv, cfg);
start_server(cfg);
while (active) {
// Sockets are set to non-blocking, therefor errno needs to be checked
// for EWOULDBLOCK and/or EAGAIN
if (cfg->proto == TCP)
{
if (((fd = accept(cfg->sockfd, (struct sockaddr *)&client, &client_len)) == -1)
&& (errno != EWOULDBLOCK) && (errno != EAGAIN))
ERROR("Error incoming connection");
}
else if (cfg->proto == UDP)
{
char buffer[MSGLENGTH];
if ((fd = recvfrom(cfg->sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client, &client_len)) == -1)
{
if (errno != EWOULDBLOCK && errno != EAGAIN)
ERROR("Error receiving message");
}
else
{
buffer[fd] = '\0';
printf("Received message from %s:%d: %s\n",
inet_ntoa(client.sin_addr), ntohs(client.sin_port), buffer);
}
}
// Only if we get a socket descriptor from the accept() call,
// we continue sending the daytime message
if (fd != -1) {
LOG("Successful incoming connection from %s:%d.\n", \
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
if (cfg->proto == TCP)
{
if (sending_datetime(fd, TCP) == -1)
ERROR("Error sending datetime");
}
else if (cfg->proto == UDP)
{
if (sending_datetime(fd, UDP) == -1)
ERROR("Error sending datetime via UDP");
}
LOG("Successfully sent datetime to %s:%d.\n", \
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
if (close(fd) == -1)
ERROR("Error on closing socket");
LOG("Successfully closing connection from %s:%d.\n", \
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
}
}
destroy_server(cfg);
free(cfg);
exit(EXIT_SUCCESS);
}
* Starting the daytime services by using create_socket() until the socket is in
* listen state.
*/
void start_server(struct srvcfg *cfg)
{
assert(cfg != NULL);
bzero(&cfg->server_addr, sizeof(cfg->server_addr));
cfg->server_addr.sin_family = AF_INET;
cfg->server_addr.sin_port = htons(cfg->port);
cfg->server_addr.sin_addr.s_addr = cfg->server_ip.s_addr;
if (cfg->proto == TCP) {
cfg->sockfd = create_socket(SOCK_STREAM, &cfg->server_addr);
// We set the socket to non-blocking, which means an accept() call won't block
// until a client connection request is received
fcntl(cfg->sockfd, F_SETFL, fcntl(cfg->sockfd, F_GETFL, 0) | O_NONBLOCK);
}
else if (cfg->proto == UDP)
{
cfg->sockfd = create_socket(SOCK_DGRAM, &cfg->server_addr); // UDP Erweiterung
}
LOG("Server started, listening on %s:%d ...\n", inet_ntoa(cfg->server_ip), cfg->port);
}
/*
* Creating a socket in listen state for the daytime service.
*/
int create_socket(int type, struct sockaddr_in *addr)
{
assert(type == SOCK_STREAM || type == SOCK_DGRAM);
assert(addr != NULL);
int sockfd;
int reuseaddr = 1;
if ((sockfd = socket(PF_INET, type, 0)) == -1)
ERROR("Error creating socket");
if ((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
&reuseaddr, sizeof(reuseaddr))) == -1)
ERROR("Error setting socket options");
if ((bind(sockfd, (struct sockaddr *)addr, sizeof(*addr))) == -1)
ERROR("Error binding socket");
if (type == SOCK_STREAM) {
if ((listen(sockfd, BACKLOG)) == -1)
ERROR("Error setting stream socket into listening mode");
}
return sockfd;
}
/*
* Sending a daytime message to the client.
*/
int sending_datetime(int fd, int port)
{
time_t curr_time;
char buffer[MSGLENGTH];
struct sockaddr_in client;
int client_len = sizeof(client);
/*
* A daytime message from this server is 26 bytes long, including a closing \r\n.
* Example: Thu Nov 26 11:29:54 2020\r\n
*/
curr_time = time(NULL);
snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&curr_time));
if (port == TCP)
{
return write(fd, buffer, strlen(buffer));
}
else if (port == UDP)
{
int n;
n = sendto(fd, buffer, strlen(buffer), 0, (struct sockaddr *)&client, client_len);
if (n < 0)
{
ERROR("ERROR IN SENDTO()"); // <---- HERE !!!!***
}
else
return n;
}
//sendto(fd, buffer, strlen(buffer), 0, (struct sockaddr *)&client, sizeof(client));
}
void destroy_server(struct srvcfg *cfg)
{
assert(cfg != NULL);
if ((close(cfg->sockfd)) == -1)
ERROR("Error closing socket");
}
1条答案
按热度按时间wz3gfoph1#
在手册页中:
成功完成后,recvfrom()应返回消息的字节长度。如果没有消息可接收,并且对等体已执行顺序关闭,则recvfrom()应返回0。否则,函数应返回-1并设置errno以指示错误。
recvfrom()
不返回文件描述符,它返回消息的长度(以字节为单位)。随后的
sendto()
操作将尝试写入recvfrom()
的返回值,这将导致该操作失败,并显示EBADF
:* 套接字参数不是有效的文件描述符。*修复:
您需要指定服务器绑定到的套接字描述符,而不是
recvfrom()
的返回值。sendto()函数通过连接模式或无连接模式的套接字发送消息,如果套接字为无连接模式,则将消息发送到dest_addr指定的地址。
将
struct srvcfg
传递到sending_daytime()
,并用cfg->sock
替换fd
。注意
time()
和ctime()
可能失败。迂腐的代码必须检查它们的返回值。