我要做的系统有一台tcp服务器和大约1000个tcp客户端。每秒1000个客户端将数据发送到tcp服务器。为了模拟这种情况,首先我从一台PC上用以下代码连接了具有50个套接字的tcp服务器。
int main() {
const char *hello = "Hello from client";
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.39");
vector<int> vec;
for ( uint8_t i = 0; i < 50; i++ ) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if ( sock < 0 ) {
cout << "... Cant Allocated Socket\n";
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
cout << "... Connection Failed \n";
return -1;
}
vec.push_back(sock);
}
for ( uint8_t i = 0; i < vec.size(); i++ ) {
send(vec[i], hello, strlen(hello), 0);
cout << "Message Send\n";
}
for ( uint8_t i = 0; i < vec.size(); i++ ) {
shutdown(vec[i], 0);
close(vec[i]);
}
return 0;
}
tcp客户端连接到tcp服务器后,它们将数据发送到tcp服务器并关闭套接字。我从终端看到,tcp客户端可以发送数据包而无需等待(少于10ms)
上面的tcp客户端代码可以成功工作,并将数据成功发送到tcp服务器。我用下面的tcp服务器代码显示来自tcp客户端的数据。
#define _DEF_TCP_SERVER_PORT 8080
#define _DEF_TCP_SERVER_MAX_QUEUE_LISTEN 12
bool finish_app = false;
struct TcpClient {
int clientSocket;
struct in_addr clientAddr;
};
vector<TcpClient> TcpClients;
struct _ServiceTcpServer {
bool enable;
int sock;
uint16_t connectedClient;
uint32_t sockLen;
sockaddr_in tcpServerAddr;
sockaddr_in remoteAddr;
};
struct _ServiceTcpServer _serviceTcpServer;
void init_tcp_server_socket() {
_serviceTcpServer.tcpServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
_serviceTcpServer.tcpServerAddr.sin_family = AF_INET;
_serviceTcpServer.tcpServerAddr.sin_port = htons(_DEF_TCP_SERVER_PORT);
_serviceTcpServer.sockLen = sizeof(_serviceTcpServer.remoteAddr);
int flag = 1;
for ( ;; ) {
_serviceTcpServer.sock = socket(AF_INET, SOCK_STREAM, 0);
if ( _serviceTcpServer.sock < 0 ) {
cout << "... Failed to allocate socket.\n";
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if ( setsockopt(_serviceTcpServer.sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) ) {
cout << "... Set SockOpt failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if( bind(_serviceTcpServer.sock, (sockaddr *) &_serviceTcpServer.tcpServerAddr, sizeof(_serviceTcpServer.tcpServerAddr)) == -1 ) {
cout << "... Socket bind failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if ( listen(_serviceTcpServer.sock, _DEF_TCP_SERVER_MAX_QUEUE_LISTEN) != 0 ) {
cout << "... Socket listen failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
break;
}
cout << "Socket init done \n";
}
void tcp_user_accept_task() {
while ( finish_app == false ) {
int temp_sck = -1;
temp_sck = accept(_serviceTcpServer.sock, (sockaddr *) &_serviceTcpServer.remoteAddr, &_serviceTcpServer.sockLen);
if ( temp_sck == -1 ) {
this_thread::sleep_for(chrono::seconds(2));
continue;
}
TcpClient tcpClient;
tcpClient.clientAddr = _serviceTcpServer.remoteAddr.sin_addr;
tcpClient.clientSocket = temp_sck;
TcpClients.push_back( tcpClient );
cout << "... New connection request: " << temp_sck << endl;
++_serviceTcpServer.connectedClient;
this_thread::sleep_for(chrono::milliseconds(50));
}
}
uint8_t temp_recv[100];
void tcp_server_run() {
while ( finish_app == false ) {
for(uint16_t i = 0 ; i < _serviceTcpServer.connectedClient; i++ ) {
int temp_cs = TcpClients[i].clientSocket;
fcntl(temp_cs, F_SETFL, O_NONBLOCK);
int temp_recvLen = recv(temp_cs, temp_recv, 20, 0);
if( temp_recvLen > 0 ) {
time_t _time = chrono::system_clock::to_time_t(chrono::system_clock::now());
cout << "Message Received At:" << ctime(&_time) << " :";
cout << temp_recv << endl;
break;
} else {
this_thread::sleep_for(chrono::milliseconds(10));
}
}
if ( temp_recv[0] == 'q' ) {
finish_app = true;
}
}
close(_serviceTcpServer.sock);
}
int main() {
thread init_thread(init_tcp_server_socket);
init_thread.join();
thread accept_thread(tcp_user_accept_task);
thread run_thread(tcp_server_run);
accept_thread.join();
run_thread.join();
return 0;
}
但是问题是,如屏幕图像所示,仅在1秒钟内收到了大约3-4个数据包。
您应该同时接收套接字,而不是查询每个套接字并在每次尚未准备好数据时休眠10ms。
正确的方法取决于平台
posix-选择
Linux-民意调查,epoll,io_submit
windows-I / O完成端口
通常,选择哪个是posix标准将足以满足您的需求。如果您想要多平台,则可能还需要探索第三方库,例如libevent和libev,这些库已经为您包装了这些平台depent调用。
快乐编码!