繁体   English   中英

c 中的 TCP 客户端/服务器

[英]TCP client/server in c

我创建了一个 TCP 客户端/服务器并提供了测试脚本,但是,除了短消息之外,所有测试都失败了。 简单地说,脚本发送客户端通过从文件重定向到服务器读取的任意消息。 但是,对于脚本随机创建的文件,它表示接收/发送端的消息不匹配。 任何帮助将不胜感激,以下是客户端和服务器代码。

// 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 字节的大小超过了典型的MTU (请注意,TCP 位于 IP 之上,后者本身是面向数据包的),因此数据很可能通过多个数据包发送。 由于您只有一次对recv的调用,因此您可能会在 TCP/IP 堆栈将后续数据包的内容放在那里之前从接收缓冲区获取第一个数据包的内容。

而是在一个循环中进行多次读取并在接收到 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