c# C语言中使用白天协议的TCP + UDP套接字

uz75evzq  于 2023-02-17  发布在  C#
关注(0)|答案(1)|浏览(173)

我的讲师给了我们他使用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");
}
wz3gfoph

wz3gfoph1#

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)

在手册页中:
成功完成后,recvfrom()应返回消息的字节长度。如果没有消息可接收,并且对等体已执行顺序关闭,则recvfrom()应返回0。否则,函数应返回-1并设置errno以指示错误。
recvfrom()不返回文件描述符,它返回消息的长度(以字节为单位)。
随后的sendto()操作将尝试写入recvfrom()的返回值,这将导致该操作失败,并显示EBADF:* 套接字参数不是有效的文件描述符。*

修复:

您需要指定服务器绑定到的套接字描述符,而不是recvfrom()的返回值。
sendto()函数通过连接模式或无连接模式的套接字发送消息,如果套接字为无连接模式,则将消息发送到dest_addr指定的地址。
struct srvcfg传递到sending_daytime(),并用cfg->sock替换fd

int sending_datetime(struct srvcfg *cfg, 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(cfg->sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&client, client_len);
        if (n < 0)
        {
            ERROR("ERROR IN SENDTO()");   
        }    
        else
            return n;
        
    }     
}

注意time()ctime()可能失败。迂腐的代码必须检查它们的返回值。

相关问题