描述:
我正在开发一个TCP echo服务器,它从TCP客户端接收数据,将其写入串行端口,然后从串行端口读回数据以将其发送到TCP客户端。为了进行测试,我使用环回连接器 (/dev/ttyUSB0)。本服务器仅供练习!
这也是一个作为 TCP 客户端服务的老化应用程序,它将数据发送到我们的服务器并计算吞吐量。
当前吞吐量约为 72KB/s,我的目标是将其提高到 80KB/s。以下是我的服务器功能和设置的简要概述:
但是,我正在努力实现所需的吞吐量。
问题:
#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;
}
任何有关如何实现目标吞吐量的指导或建议将不胜感激。
我已经实现了高/低水位线来控制串口的写入频率,但我仍然无法达到预期的吞吐量。
我认为串行链路是瓶颈,大多数时候
enable_write
是false
。
当
enable_write
为 false
并且 client_fd
可读时,则 while 循环繁忙。 select()
立即返回,直到有注释可供读取 client_fd
。
可能会影响性能。至少它使用了大量的CPU时间。
对此的简单修复:
请勿将
client_fd
设置为 read_fd
- 当 enable_write
为 false
时设置。