socket编程 ------ 建立 TCP 服务器和客户端流程(阻塞方式)
服务器端:
服务器端先创建一个socket,然后把这个socket绑定到端口上,接着让它向tcp/ip协议栈请求一个监听服务并创建一个accept队列来接受客户端请求。
void creat_tcpServer() { int32 listenfd; /* Create socket for incoming connections */ do{ //AF_INET(又称 PF_INET)是 IPv4 网络协议的套接字类型 //SOCK_STREAM 指TCP通信
//0:系统根据地址格式和套接字类型,自动选择一个合适的协议,因为这里是TCP,也可填IPPROTO_TCP
listenfd = socket(AF_INET, SOCK_STREAM, 0); if(listenfd == -1) { vTaskDelay(1000/portTICK_RATE_MS); } }while(listenfd == -1); struct sockaddr_in server_addr; int32 ret; /* Construct local address structure */ memset(&server_addr, 0, sizeof(server_addr));/* Zero out structure */ server_addr.sin_family = AF_INET; /* Internet address family */ server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_len = sizeof(server_addr); server_addr.sin_port = htons(9999); /* Local port */ //绑定socket:将创建的socket绑定到本地的IP地址和端口,此socket是半相关的,只是负责 //侦听客户端的连接请求,并不能用于和客户端通信 do{ ret = bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (ret != 0) { vTaskDelay(1000/portTICK_RATE_MS); } }while(ret != 0); //listen侦听: 第一个参数是套接字,第二个参数为accept队列大小,当服务器接收到第三次握手后 //将连接放到这个队列中,直到被accept处理才清除。当accept队列满了之后,即使client继续向server发 //送ACK的包,也会不被响应,此时ListenOverflows+1,同时server通\ //过/proc/sys/net/ipv4/tcp_abort_on_overflow(linux kernel 2.2之后)来\ //决定如何返回,0表示直接丢弃该ACK,1表示发送RST通知client;相\ //应的,client则会分别返回read timeout 或者 connection reset by peer。 do{ /* Listen to the local connection */ ret = listen(listenfd, 1); if(ret != 0) { vTaskDelay(1000/portTICK_RATE_MS); } }while(ret != 0); int32 client_socket; int32 len = sizeof(struct sockaddr_in); int32 recbytes; struct sockaddr_in remote_addr; for(;;) { /*block here waiting remote connect request*/ if((client_socket = accept(listenfd, (struct sockaddr *)&remote_addr, (socklen_t *)&len)) < 0) { continue; } char *recv_buf = (char *)zalloc(129); while ((recbytes = read(client_socket , recv_buf, 128)) > 0) { //数据保存在recv_buf } free(recv_buf); if(recbytes <= 0) { close(client_socket); } } }
客户端:
for(;;) { sta_socket = socket(PF_INET, SOCK_STREAM, 0); if (-1 == sta_socket) { close(sta_socket); vTaskDelay(1000/portTICK_RATE_MS); printf("ESP8266 TCP client task > socket fail!\n"); continue; } printf("ESP8266 TCP client task > socket ok!\n");
//开启keepalive
keepalive = 1; if(setsockopt(sta_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive)) < 0) { printf("set keepalive fail"); } bzero(&remote_ip, sizeof(struct sockaddr_in)); remote_ip.sin_family = AF_INET; remote_ip.sin_addr.s_addr = inet_addr(server_ip); remote_ip.sin_port = htons(server_port); if(0 != (connect_status = connect(sta_socket, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)))) { close(sta_socket); vTaskDelay(1000/portTICK_RATE_MS); printf("ESP8266 TCP client task > connect fail!\n"); continue; } printf("ESP8266 TCP client task > connect ok!\n");
//如该连接在60秒没有任何数据往来,则进行探测 keepidle = 60 setsockopt(sta_socket, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle));
//探测时发包的时间间隔为5秒
keepinterval = 5; setsockopt(sta_socket, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval));
//探测尝试的次数,如果第1次探测包就收到响应,则后2次的不再发
keepcount = 3; setsockopt(sta_socket, IPPROTO_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount)); setsockopt(sta_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flag , sizeof(flag)); while ((recbytes = read(sta_socket , recv_buf, 32)) > 0) {
handle_server_data(recv_buf, recbytes); } if(recbytes <= 0) { close(sta_socket); printf("ESP8266 TCP client task > server closed!\n"); } }
标签:
网络通信知识
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)