如何增加 TCP 服务器(通过 TCP 提供串行端口)吞吐量?

问题描述 投票:0回答:1

描述:

我正在开发一个TCP echo服务器,它从TCP客户端接收数据,将其写入串行端口,然后从串行端口读回数据以将其发送到TCP客户端。为了进行测试,我使用环回连接器 (/dev/ttyUSB0)。本服务器仅供练习!

这也是一个作为 TCP 客户端服务的老化应用程序,它将数据发送到我们的服务器并计算吞吐量。

当前吞吐量约为 72KB/s,我的目标是将其提高到 80KB/s。以下是我的服务器功能和设置的简要概述:

  1. 服务器侦听传入的 TCP 连接并使用非阻塞套接字处理它们。
  2. 从 TCP 客户端接收到的数据被写入串行端口。
  3. 从串口读取的数据被发送回TCP客户端。
  4. 我已经实现了高/低水位线来控制何时暂停或恢复写入串行端口。 (由于TCP和串行之间的吞吐量差异

但是,我正在努力实现所需的吞吐量


问题:

  1. 我可以应用哪些策略或优化来提高服务器的吞吐量?
  2. 我当前的实施是否存在任何潜在的瓶颈?
  3. 是否有更有效的方法来处理串口和TCP连接以提高性能?

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/select.h>
#include <termios.h>
#include <sys/ioctl.h>
 
#define BACK_LOG 5
#define BUFFER_SIZE 8192
#define SERIAL_BUFFER_SIZE 2048
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
 
typedef struct sockaddr_in sockaddr_in;
typedef struct tcp_info
{
    int fd;
    sockaddr_in socket_address;
} tcp_info;
 
void tcp_destroy_server(tcp_info *tcp_server)
{
    if (tcp_server == NULL)
    {
        return;
    }
 
    close(tcp_server->fd);
    free(tcp_server);
}
 
tcp_info *tcp_create_server(const char *ip_addr, int port)
{
    tcp_info *tcp_server;
    int yes = 1;
 
    if (ip_addr == NULL)
    {
        return NULL;
    }
 
    if ((tcp_server = calloc(1, sizeof(tcp_info))) == NULL)
    {
        return NULL;
    }
 
    tcp_server->socket_address.sin_port = htons(port);
    tcp_server->socket_address.sin_family = AF_INET;
    inet_pton(AF_INET, ip_addr, &(tcp_server->socket_address.sin_addr));
 
    if ((tcp_server->fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("[ERROR] Failed to create socket!\n");
        return NULL;
    }
 
    if (setsockopt(tcp_server->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
    {
        perror("Set socket option failed");
        close(tcp_server->fd);
        return NULL;
    }
 
    if (bind(tcp_server->fd, (const struct sockaddr *)&tcp_server->socket_address, sizeof(tcp_server->socket_address)) == -1)
    {
        printf("[ERROR] Failed to bind socket!\n");
        close(tcp_server->fd);
        return NULL;
    }
 
    if (listen(tcp_server->fd, BACK_LOG) == -1)
    {
        printf("[ERROR] Failed to listen!\n");
        close(tcp_server->fd);
        return NULL;
    }
 
    return tcp_server;
}
 
int serial_open_port(const char *serial_port)
{
    int serial_fd = -1;
    struct termios tty = {0};
 
    if (serial_port == NULL)
    {
        return -1;
    }
 
    if ((serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY)) == -1)
    {
        return -1;
    }
 
    if (tcgetattr(serial_fd, &tty) != 0)
    {
        perror("Error from tcgetattr");
        close(serial_fd);
        return -1;
    }
 
    cfsetospeed(&tty, B921600);
    cfsetispeed(&tty, B921600);
 
    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_iflag &= ~IGNBRK;
    tty.c_lflag = 0;
    tty.c_oflag = 0;
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~(PARENB | PARODD);
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag |= CRTSCTS;
 
    if (tcsetattr(serial_fd, TCSANOW, &tty) != 0)
    {
        perror("Error from tcsetattr");
        close(serial_fd);
        return -1;
    }
 
    return serial_fd;
}
 
// receiving buffer
int get_serial_input_buffer_size(int fd)
{
    int bytes_available = -1;
 
    if (ioctl(fd, TIOCINQ, &bytes_available) == -1)
    {
        perror("ioctl TIOCINQ error");
        return -1;
    }
 
    return bytes_available;
}
 
// sending buffer
int get_serial_output_buffer_size(int fd)
{
    int bytes_available = -1;
 
    if (ioctl(fd, TIOCOUTQ, &bytes_available) == -1)
    {
        perror("ioctl TIOCOUTQ error");
        return -1;
    }
 
    return bytes_available;
}
 
int main(int argc, char *argv[])
{
    if (argc != 4)
    {
        printf("Usage: %s <IP> <PORT> <SERIAL PORT>\n", argv[0]);
        return EXIT_FAILURE;
    }
 
    /* Server configuration */
    const char *server_address = argv[1];
    int port = atoi(argv[2]);
    const char *serial_port = argv[3];
 
    /* Define variables */
    tcp_info *tcp_server;
    sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    char socket_buffer[BUFFER_SIZE] = {0};
    char serial_buffer[SERIAL_BUFFER_SIZE] = {0};
    bool enable_write = true;
 
    int client_fd = -1;
    int serial_fd = -1;
    int serial_written = 0;
    int serial_offset = 0;
 
    if ((tcp_server = tcp_create_server(server_address, port)) == NULL)
    {
        perror("[ERROR] failed to create TCP server");
        return EXIT_FAILURE;
    }
 
    if ((serial_fd = serial_open_port(serial_port)) == -1)
    {
        perror("[ERROR] failed to open serial");
        tcp_destroy_server(tcp_server);
        return EXIT_FAILURE;
    }
 
    printf("[INFO] Server waiting for connections.........\n");
    memset(&client_addr, 0, sizeof(client_addr));
 
    if ((client_fd = accept(tcp_server->fd, (struct sockaddr *)&client_addr, &client_addr_len)) == -1)
    {
        perror("[ERROR] Failed to aceept a new socket connection");
        tcp_destroy_server(tcp_server);
        return EXIT_FAILURE;
    }
 
    printf("[INFO] Accept a connection from tcp \n");
 
    fd_set read_fds;
    fcntl(client_fd, F_SETFL, O_NONBLOCK);
 
    while (1)
    {
        FD_ZERO(&read_fds);
        FD_SET(client_fd, &read_fds);
        FD_SET(serial_fd, &read_fds);
 
        if (select(MAX(serial_fd, client_fd) + 1, &read_fds, NULL, NULL, NULL) == -1)
        {
            perror("select() error");
            break;
        }
 
        /* Read data from socket , then writing to the serial port */
        if (enable_write && FD_ISSET(client_fd, &read_fds))
        {
            int valread = recv(client_fd, socket_buffer, BUFFER_SIZE, 0);
 
            if (valread <= 0)
            {
                perror("[ERROR] Socket recv error");
                break;
            }
 
            int write_num = write(serial_fd, socket_buffer, valread);
           
            if (write_num < 0)
            {
                perror("[ERROR] Write to serial error");
                break;
            }
            else if(write_num != valread)
            {
                perror("write num != valread");
                break;
            }
           
        }
       
        /* high/low watermark */
        int val = get_serial_output_buffer_size(serial_fd);
        if (val >= 23000)
        {
            enable_write = false; // stop writing data to serial
        }
        else if (val <= 1000)
        {
            //printf("val size = %d", val);
            enable_write = true;
        }
 
        /* Read data from serial */
        if (FD_ISSET(serial_fd, &read_fds))
        {  
            int read_num = read(serial_fd, serial_buffer + serial_offset, SERIAL_BUFFER_SIZE - serial_offset);
 
            if (read_num <= 0)
            {
                perror("[ERROR] Read from serial");
                break;
            }
 
            serial_offset += read_num;
 
            if (serial_offset == SERIAL_BUFFER_SIZE)
            {
                if (send(client_fd, serial_buffer, SERIAL_BUFFER_SIZE, 0) == -1)
                {
                    perror("[ERROR] Socket send error");
                    break;
                }
                printf("socket send %d bytes\n", SERIAL_BUFFER_SIZE);
                serial_offset = 0;
            }
        }
    }
 
    tcp_destroy_server(tcp_server);
    return 0;
}

任何有关如何实现目标吞吐量的指导或建议将不胜感激。

我已经实现了高/低水位线来控制串口的写入频率,但我仍然无法达到预期的吞吐量。

c linux sockets tcp serial-port
1个回答
0
投票

我认为串行链路是瓶颈,大多数时候

enable_write
false

enable_write
false
并且
client_fd
可读时,则 while 循环繁忙。
select()
立即返回,直到有注释可供读取
client_fd

可能会影响性能。至少它使用了大量的CPU时间。

对此的简单修复:

请勿将

client_fd
设置为
read_fd
- 当
enable_write
false
时设置。

© www.soinside.com 2019 - 2024. All rights reserved.