TCP服务器网络通信实验,使用RT-Thread嵌入式实时操作系统实现TCP服务器连接多个客户端同时通信实验,详情实现原理请查看代码注释。
代码实现功能:通过RT-Thread finsh控制台执行tcpserv_multi开启TCP服务器,可以同时连接多个客户端进行数据通信,客户端连接成功之后发送数据给服务器,服务器收到客户端的数据之后通过控制台打印显示收到的数据并且返回给客户端。客户端发送q、Q、exit断开客户端连接。
实验图片如下图:TCP服务器连接多客户端同时通信实验图
/* 程序清单:tcp 服务端
* 这是一个 tcp 服务端的例程,可以连接多个客户端同时进行数据通信
* 导出 tcpserv_multi 命令到控制终端
* 命令调用格式:tcpserv_multi
* 无参数
* 程序功能:作为一个服务端,接收并显示客户端发来的数据 ,接收到q、Q、 exit 退出程序
*/
#include <rtthread.h>
#include <sys/socket.h> /* 使用BSD socket,需要包含socket.h头文件 */
#include <netdb.h>
#include <string.h>
#include <finsh.h>
#include <sys/errno.h>
#define BUFSZ (128)
static rt_bool_t stop = RT_FALSE; /* 停止标志 */
void tcpClientConThread(void *arg){
int connected = *(int *)arg;
char *recv_data;
int bytes_received = 0;
int ret;
rt_kprintf("new connection from %d\n",connected);
recv_data = rt_malloc(BUFSZ + 1); /* 分配接收用的数据缓冲 */
if (recv_data == RT_NULL)
{
rt_kprintf("No memory\n");
return;
}
while(1)
{
bytes_received = recv(connected, (void *)recv_data, BUFSZ,0);
if (bytes_received <= 0)
{
/* 打印recv函数返回值为小于等于0的警告信息 */
rt_kprintf("\nReceived warning,recv function return %d.\r\n",bytes_received);
/* 接收失败,关闭这个connected socket */
if((ETIMEDOUT != errno) && (EWOULDBLOCK != errno))
{
closesocket(connected);
rt_kprintf("TCP client disconnect !\n");
rt_free(recv_data);
return;
}
continue;
}
/* 有接收到数据,把末端清零 */
recv_data[bytes_received] = '\0';
if (strcmp(recv_data, "q") == 0 || strcmp(recv_data, "Q") == 0)
{
/* 如果是首字母是q或Q,关闭这个连接 */
closesocket(connected);
rt_free(recv_data);
return;
}
else if (strcmp(recv_data, "exit") == 0)
{
/* 如果接收的是exit,则关闭整个服务端 */
closesocket(connected);
stop = RT_TRUE;
rt_free(recv_data);
return;
}
else
{
/* 在控制终端显示收到的数据 */
rt_kprintf("TCP server rec from %d: %s\n",connected,recv_data);
}
//把接收到的数据发送回到客户端
ret = send(connected, (const void *)recv_data, bytes_received,0);
if (ret < 0)
{
/* 发送失败,关闭这个连接 */
closesocket(connected);
rt_kprintf("\nsend error,close the socket.\r\n");
rt_free(recv_data);
return;
}
else if (ret == 0)
{
/* 打印send函数返回值为0的警告信息 */
rt_kprintf("\n Send warning,send function return 0.\r\n");
}
}
}
static void tcpserv_multi(int argc, char **argv)
{
socklen_t sin_size;
int sock, connected;
struct sockaddr_in server_addr, client_addr;
int ret=-1;
rt_thread_t tid; /* 线程句柄 */
stop = RT_FALSE;
/* 一个socket在使用前,需要预先创建出来,指定SOCK_STREAM为TCP的socket */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* 创建失败的错误处理 */
rt_kprintf("Socket error\n");
return;
}
/* 初始化服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5000); /* 服务端工作的端口 */
server_addr.sin_addr.s_addr = INADDR_ANY;
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/*
增加地址复用
*/
ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ret, sizeof(int));
if(ret < 0){
rt_kprintf("TCP reuseADDR error!\n");
return;
}
/* 绑定socket到服务端地址 */
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
/* 绑定失败 */
rt_kprintf("Unable to bind\n");
return;
}
/* 在socket上进行监听 */
if (listen(sock, 5) == -1)
{
rt_kprintf("Listen error\n");
return;
}
rt_kprintf("\nTCPServer Waiting for client on port 5000...\n");
while (stop != RT_TRUE)
{
sin_size = sizeof(struct sockaddr_in);
/* 接受一个客户端连接socket的请求,这个函数调用是阻塞式的 */
connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
/* 返回的是连接成功的socket */
if (connected < 0)
{
rt_kprintf("accept connection failed! errno = %d\n", errno);
continue;
}
/* 接受返回的client_addr指向了客户端的地址信息 */
rt_kprintf("I got a connection from (%s , %d) %d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port),connected);
tid=rt_thread_create("client",tcpClientConThread,(void*)&connected,1024,4,10);
rt_thread_startup(tid);
}
/* 退出服务 */
closesocket(sock);
rt_kprintf("closesocket errno = %d\n", errno);
return ;
}
MSH_CMD_EXPORT(tcpserv_multi, a tcp server multi connect sample);