简体   繁体   English

c 中的 TCP 客户端/服务器

[英]TCP client/server in c

I created a TCP client/server and was provided test script, however, beyond short messages, all tests are failing.我创建了一个 TCP 客户端/服务器并提供了测试脚本,但是,除了短消息之外,所有测试都失败了。 Simply, the script send arbitrary messages that the client reads through redirection from a file to ther server.简单地说,脚本发送客户端通过从文件重定向到服务器读取的任意消息。 However with randomly created files by the script, it says that the messages on receving/sending side do not match.但是,对于脚本随机创建的文件,它表示接收/发送端的消息不匹配。 Any help will be appreciated, below is the client and server code.任何帮助将不胜感激,以下是客户端和服务器代码。

// server.c

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

#define QUEUE_LENGTH 10
#define RECV_BUFFER_SIZE 2048

/* TODO: server()
 * Open socket and wait for client to connect
 * Print received message to stdout
 * Return 0 on success, non-zero on failure
*/
int server(char *server_port) {
    int sockfd, new_fd;
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage their_addr;   // connector's address 
    socklen_t sin_size;
    int yes = 1;
    char s[INET6_ADDRSTRLEN];
    int rv;

    char buff[RECV_BUFFER_SIZE];
    int numBytes;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // use my ip address

    if ((rv = getaddrinfo(NULL, server_port, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("server: socket");
            continue;
        }

        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    freeaddrinfo(servinfo); // all done with this structure
    
    if (p == NULL) {
        fprintf(stderr, "server: failed to bind\n");
        exit(1);
    }

    if (listen(sockfd, QUEUE_LENGTH) == -1) {
        perror("listen");
        exit(1);
    }

    // printf("server: waiting for connections...\n");

    while (1) {
        sin_size = sizeof their_addr;
        if((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
            perror("accept");
            continue;
        }

        if (!fork()) { // child process
            close(sockfd);    // child does not need the listener
            if ((numBytes = recv(new_fd, buff, RECV_BUFFER_SIZE -1, 0)) == -1) {
                perror("recv");
                exit(1);
            }

            buff[numBytes] = '\0';
            printf("%s", buff);

            close(new_fd);
            exit(0);
        }
        close(new_fd);
    }
    return 0;
}

/*
 * main():
 * Parse command-line arguments and call server function
*/
int main(int argc, char **argv) {
  char *server_port;

  if (argc != 2) {
    fprintf(stderr, "Usage: ./server-c [server port]\n");
    exit(EXIT_FAILURE);
  }

  server_port = argv[1];
  return server(server_port);
}
// client.c

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

#define SEND_BUFFER_SIZE 2048

/* TODO: client()
 * Open socket and send message from stdin.
 * Return 0 on success, non-zero on failure
*/
int client(char *server_ip, char *server_port)
{
    int sockfd;
    int status;
    struct addrinfo hints, *servinfo, *p;

    char send_buff[SEND_BUFFER_SIZE];
    int numbytes;
    char s[INET6_ADDRSTRLEN];

    // getaddrinfo
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((status = getaddrinfo(server_ip, server_port, &hints, &servinfo)) != 0)
    {
        fprintf(stderr, "getadrrinfo: %s\n", gai_strerror(status));
        return 1;
    }

    for (p = servinfo; p != NULL; p = p->ai_next)
    {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                             p->ai_protocol)) == -1)
        {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("client: socket");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    freeaddrinfo(servinfo);

    // reading from stdin into send_buff, then send
    if((numbytes = read(0, send_buff, SEND_BUFFER_SIZE)) != -1) {
        if (send(sockfd, send_buff, numbytes, 0) == -1) {
            perror("send");
            exit(1);
        }
    }

    close(sockfd);

    return 0;
}

/*
 * main()
 * Parse command-line arguments and call client function
*/
int main(int argc, char **argv)
{
    char *server_ip;
    char *server_port;

    if (argc != 3)
    {
        fprintf(stderr, "Usage: ./client-c [server IP] [server port] < [message]\n");
        exit(EXIT_FAILURE);
    }

    server_ip = argv[1];
    server_port = argv[2];
    return client(server_ip, server_port);
}

2048 bytes exceed in size the typical MTU (note that TCP is laid over IP, which itself is packet oriented), so data is likely sent via multiple packets. 2048 字节的大小超过了典型的MTU (请注意,TCP 位于 IP 之上,后者本身是面向数据包的),因此数据很可能通过多个数据包发送。 As you have only one single call to recv chances are that you fetch the contents of first packet from the receive buffer before the the TCP/IP stack could place the contents of the follow up packets there.由于您只有一次对recv的调用,因此您可能会在 TCP/IP 堆栈将后续数据包的内容放在那里之前从接收缓冲区获取第一个数据包的内容。

Rather have multiple reads in a loop and exit the loop on receiving 0 bytes (remote socket closed):而是在一个循环中进行多次读取并在接收到 0 个字节时退出循环(远程套接字关闭):

while((numBytes = recv(new_fd, buff, RECV_BUFFER_SIZE -1, 0)) > 0)
{
    buff[numBytes] = '\0';
    printf("%s", buff);
}

if(numBytes < 0)
{
    // error handling
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM