【问题标题】:Linux TCP server, sending RAW data to few clients every X secondsLinux TCP 服务器,每 X 秒向少数客户端发送 RAW 数据
【发布时间】:2013-07-29 06:58:48
【问题描述】:

我正在编写简单的 TCP 服务器,但发现了一些问题。也许你能帮我一点忙。 所以,我先写了一个回显服务器(测试与计算机客户端的连接)。它工作正常,但现在我需要稍微改变一下。服务器应在连接时向客户端发送 char[100],并每隔 X 秒/分钟向每个客户端发送相同的 char[]。

我尝试了很多更改,但应用程序只会崩溃。在这段代码中评论了我的一些“错误”。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>

/* BufferLength is 100 bytes */
#define BufferLength 100
/* Server port */
#define SERVPORT 6000



int main(){

/* Variable and structure definitions. */

int sd, wyslij, sd2, rc, length = sizeof(int);
int totalcnt = 0, on = 1;
char temp;
char buffer[BufferLength];
struct sockaddr_in serveraddr;
struct sockaddr_in their_addr;

fd_set read_fd;
struct timeval timeout;
timeout.tv_sec = 15;
timeout.tv_usec = 0;

char datadata[100] = "This is a test string from server lol!!! ";

/* Get a socket descriptor */
if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    perror("Server-socket() error");
    /* exit */
    exit (-1);
}else
    printf("Server-socket() is OK\n");

/* Allow socket descriptor to be reusable */

if((rc = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0){
    perror("Server-setsockopt() error");
    close(sd);
    exit (-1);
}else
    printf("Server-setsockopt() is OK\n");


    /* bind to an address */
memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVPORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

printf("Using %s, listening at %d\n", inet_ntoa(serveraddr.sin_addr), SERVPORT);


if((rc = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0){
    perror("Server-bind() error");
    /* Close the socket descriptor */
    close(sd);
    /* and just exit */
    exit(-1);
}else
    printf("Server-bind() is OK\n");

/* queue up to 10 clients  */

if((rc = listen(sd, 10)) < 0){
    perror("Server-listen() error");
    close(sd);
    exit (-1);
}else
    printf("Server-Ready for client connection...\n");

/* accept() the incoming connection request. */
int sin_size = sizeof(struct sockaddr_in);

if((sd2 = accept(sd, (struct sockaddr *)&their_addr, &sin_size)) < 0){
    perror("Server-accept() error");
    close(sd);
    exit (-1);
}else
    printf("Server-accept() is OK\n");

/*client IP*/

printf("Server-new socket, sd2 is OK...\n");
printf("Got connection from the client: %s\n", inet_ntoa(their_addr.sin_addr));

/* Wait for up to 15 seconds on */
/* select() for data to be read. */

FD_ZERO(&read_fd);
FD_SET(sd2, &read_fd);
rc = select(sd2+1, &read_fd, NULL, NULL, &timeout);


/* rc = write(sd2, datadata, sizeof(datadata)); */
if((rc == 1) && (FD_ISSET(sd2, &read_fd))){

/* rc = write(sd2, datadata, sizeof(datadata)); */

/* Read data from the client. */
totalcnt = 0;

    while(totalcnt < BufferLength){
    /* read() from client */

        rc = read(sd2, &buffer[totalcnt], (BufferLength - totalcnt));

        if(rc < 0){
            perror("Server-read() error");
            close(sd);
            close(sd2);
            exit (-1);
        }else if (rc == 0){
            printf("Client program has issued a close()\n");
            close(sd);
            close(sd2);
            exit(-1);
        }
        else{
            totalcnt += rc;
            printf("Server-read() is OK\n");
        }
    }
}else if (rc < 0){
    perror("Server-select() error");
    close(sd);
    close(sd2);
    exit(-1);
}
/* rc == 0 */
else{
    printf("Server-select() timed out.\n");
    close(sd);
    close(sd2);
    exit(-1);
}


/* Shows the data */

printf("Received data from the client: %s\n", buffer);

/* write() some bytes of string, */
/* back to the client. */

printf("Server-Echoing back to client...\n");
rc = write(sd2, datadata, sizeof(datadata));

if(rc != totalcnt){
    perror("Server-write() error");
    /* Get the error number. */
    rc = getsockopt(sd2, SOL_SOCKET, SO_ERROR, &temp, &length);

    if(rc == 0){
        /* Print out the asynchronously */
        /* received error. */
        errno = temp;
        perror("SO_ERROR was: ");
    }else
        printf("Server-write() is OK\n");
        close(sd);
        close(sd2);
        exit(-1);
    }

/* Close the connection to the client and */
/* close the server listening socket. */

close(sd2);
close(sd);
exit(0);

return 0;
}

非常感谢朋友!

【问题讨论】:

  • 它在哪里崩溃?您是否在 gdb 或其他调试器中运行过它?
  • 首先你应该学习如何使用调试器,例如GNU debugger。如果您使用调试信息(GCC/clang 的-g 标志)进行编译并在调试器中运行您的程序,则程序将在崩溃发生时停止。然后,您可以使用bt 命令显示函数回溯,以查看您是如何到达崩溃现场的。如果它不在您的某个函数中,请使用up 命令沿调用堆栈向上走,直到您位于其中一个函数中。在那里您可以使用p variable 打印变量的值。检查例如NULL 指针。
  • 当我取消注释行 rc = write(sd2, datadata, sizeof(datadata));上面代码的两处被注释了
  • 快速查看,您的write 调用都不会导致崩溃。请编辑您的问题以包含崩溃的回溯(即bt 命令的输出)。
  • 我想知道您的服务器是否在客户端进程关闭其 TCP 连接时发出 SIGPIPE 信号?如果是这样,您可以抑制或按此处所述:stackoverflow.com/questions/11302873/preventing-sigpipe

标签: c linux tcp


【解决方案1】:

您可能想看看 D.J. Bernstein 的 tcpserver(参见 http://cr.yp.to/ucspi-tcp/tcpserver.html)。基本上,您可以简单地在 tcpserver 下运行您的 C 程序,tcpserver 将处理所有事情,包括设置套接字、列出您正在使用的任何端口上的传入连接等。当传入连接到达您指定的端口时, tcpserver 将生成程序的一个实例,并将来自客户端的传入信息通过管道传输到程序的 STDIN,并将来自程序的 STDOUT 的传出信息传输回客户端。这样,您可以专注于程序的核心逻辑(并简单地读取/写入 stdout/stdin),并让 tcpserver 处理所有繁重的工作,例如套接字等。

【讨论】:

  • 好东西!我经过几次测试,这太棒了。感谢您的帮助,我真的很感激!
【解决方案2】:

好吧,我在一个简单的 TCP 客户端代码上运行了你的程序,没有看到任何崩溃。因此,您可能应该向其中添加 gdb 信息。另外,在程序中,我看不到您的程序在哪里定期唤醒(您确实有评论)并将数据发送到客户端。您还应该考虑将客户端 fd 添加到读取 fd 集的列表中,并进行一个常见的 select() 调用。如果 select() 在侦听器上返回一个读取事件,那么这是一个新连接,您应该调用 accept。如果 select() 在子 fd 上返回读取事件,那么您有一些数据要读取,您应该调用 recv()/read()。

【讨论】:

    猜你喜欢
    • 2015-12-26
    • 1970-01-01
    • 2014-03-08
    • 2021-02-21
    • 2015-02-10
    • 2019-01-01
    • 2019-07-24
    • 2014-09-07
    • 2016-07-21
    相关资源
    最近更新 更多