C语言网络编程:TCP/IP协议栈、套接字、服务器/客户端通信深度解析

C语言网络编程:TCP/IP协议栈、套接字、服务器/客户端通信深度解析

C语言网络编程:TCP/IP协议栈、套接字、服务器/客户端通信深度解析

在这里插入图片描述

一、前言:为什么网络编程是C语言开发的重要技能?

学习目标

  • 理解网络编程的本质:编写程序实现不同设备之间的网络通信
  • 明确网络编程的重要性:支撑互联网、物联网、云计算等应用的基础
  • 掌握本章学习重点:TCP/IP协议栈、套接字、服务器/客户端通信的开发方法、避坑指南、实战案例分析
  • 学会使用C语言开发网络应用,实现数据传输和网络交互

重点提示

💡 网络编程是C语言开发的重要技能!互联网和物联网的普及,使得网络编程成为程序员的必备技能,C语言的高性能和可移植性使其在网络编程中具有重要地位。


二、模块1:TCP/IP协议栈基础

2.1 学习目标

  • 理解TCP/IP协议栈的本质:用于网络通信的协议集合,分为应用层、传输层、网络层、数据链路层
  • 掌握TCP/IP协议栈的结构:各层协议的功能和交互
  • 掌握TCP/IP协议栈的常用协议:TCP、UDP、IP、HTTP、FTP等
  • 掌握TCP/IP协议栈的避坑指南:避免协议栈配置错误、避免数据传输错误、避免连接失败
  • 避开TCP/IP协议栈使用的3大常见坑

2.2 TCP/IP协议栈的结构

应用层:提供用户服务,如HTTP、FTP、SMTP等
传输层:提供端到端的通信,如TCP、UDP
网络层:提供路由和转发功能,如IP、ICMP
数据链路层:提供物理连接和帧传输,如以太网、WiFi

2.3 TCP/IP协议栈的常用协议

TCP协议

  • 可靠的、面向连接的传输协议
  • 重传机制、滑动窗口、拥塞控制
  • 适合要求高可靠性的应用,如HTTP、FTP

UDP协议

  • 不可靠的、无连接的传输协议
  • 不需要建立连接,传输速度快
  • 适合要求高传输速度的应用,如视频会议、游戏

三、模块2:套接字编程基础

3.1 学习目标

  • 理解套接字的本质:网络通信的端点,用于实现服务器和客户端之间的通信
  • 掌握套接字编程的基本步骤:创建套接字、绑定地址、监听连接、接受连接、发送和接收数据、关闭套接字
  • 掌握套接字编程的避坑指南:避免地址绑定错误、避免连接失败、避免数据传输错误
  • 避开套接字编程使用的3大常见坑

3.2 套接字编程的基本步骤

创建套接字

intsocket(int domain,int type,int protocol);

绑定地址

intbind(int sockfd,conststructsockaddr*addr,socklen_t addrlen);

监听连接

intlisten(int sockfd,int backlog);

接受连接

intaccept(int sockfd,structsockaddr*addr,socklen_t*addrlen);

发送和接收数据

ssize_tsend(int sockfd,constvoid*buf,size_t len,int flags);ssize_trecv(int sockfd,void*buf,size_t len,int flags);

关闭套接字

intclose(int sockfd);

四、模块3:服务器/客户端通信开发

4.1 学习目标

  • 理解服务器/客户端通信的本质:服务器监听连接,客户端发起连接,实现数据传输
  • 掌握服务器/客户端通信的开发方法:使用TCP或UDP协议
  • 掌握服务器/客户端通信的避坑指南:避免并发连接处理错误、避免数据传输错误、避免连接超时
  • 避开服务器/客户端通信使用的3大常见坑

4.2 服务器/客户端通信的开发方法

TCP服务器

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#definePORT8080#defineBUFFER_SIZE1024intmain(){int server_fd, new_socket;structsockaddr_in address;int opt =1;int addrlen =sizeof(address);char buffer[BUFFER_SIZE]={0};char*hello ="Hello from server";// 创建套接字文件描述符if((server_fd =socket(AF_INET, SOCK_STREAM,0))==0){perror("socket() error");exit(EXIT_FAILURE);}// 设置套接字选项if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt,sizeof(opt))){perror("setsockopt() error");exit(EXIT_FAILURE);} address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port =htons(PORT);// 绑定套接字到指定地址和端口if(bind(server_fd,(structsockaddr*)&address,sizeof(address))<0){perror("bind() error");exit(EXIT_FAILURE);}// 监听连接if(listen(server_fd,3)<0){perror("listen() error");exit(EXIT_FAILURE);}// 接受连接if((new_socket =accept(server_fd,(structsockaddr*)&address,(socklen_t*)&addrlen))<0){perror("accept() error");exit(EXIT_FAILURE);}// 接收数据read(new_socket, buffer, BUFFER_SIZE);printf("收到客户端消息:%s\n", buffer);// 发送数据send(new_socket, hello,strlen(hello),0);printf("向客户端发送消息:%s\n", hello);// 关闭套接字close(new_socket);close(server_fd);return0;}

TCP客户端

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#definePORT8080#defineBUFFER_SIZE1024intmain(){int sock =0;structsockaddr_in serv_addr;char buffer[BUFFER_SIZE]={0};char*hello ="Hello from client";// 创建套接字文件描述符if((sock =socket(AF_INET, SOCK_STREAM,0))<0){perror("socket() error");exit(EXIT_FAILURE);} serv_addr.sin_family = AF_INET; serv_addr.sin_port =htons(PORT);// 将IPv4和IPv6地址从文本转换为二进制形式if(inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr)<=0){perror("无效的地址/地址不可支持");exit(EXIT_FAILURE);}// 连接服务器if(connect(sock,(structsockaddr*)&serv_addr,sizeof(serv_addr))<0){perror("连接失败");exit(EXIT_FAILURE);}// 发送数据send(sock, hello,strlen(hello),0);printf("向服务器发送消息:%s\n", hello);// 接收数据read(sock, buffer, BUFFER_SIZE);printf("收到服务器消息:%s\n", buffer);// 关闭套接字close(sock);return0;}

UDP服务器

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#definePORT8080#defineBUFFER_SIZE1024intmain(){int sockfd;structsockaddr_in servaddr, cliaddr;int opt =1;int len;char buffer[BUFFER_SIZE]={0};char*hello ="Hello from UDP server";// 创建套接字文件描述符if((sockfd =socket(AF_INET, SOCK_DGRAM,0))<0){perror("socket() error");exit(EXIT_FAILURE);}// 设置套接字选项if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt,sizeof(opt))){perror("setsockopt() error");exit(EXIT_FAILURE);} servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = INADDR_ANY; servaddr.sin_port =htons(PORT);// 绑定套接字到指定地址和端口if(bind(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr))<0){perror("bind() error");exit(EXIT_FAILURE);} len =sizeof(cliaddr);// 接收数据recvfrom(sockfd, buffer, BUFFER_SIZE,0,(structsockaddr*)&cliaddr,&len);printf("收到客户端消息:%s\n", buffer);// 发送数据sendto(sockfd, hello,strlen(hello),0,(structsockaddr*)&cliaddr, len);printf("向客户端发送消息:%s\n", hello);// 关闭套接字close(sockfd);return0;}

UDP客户端

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#definePORT8080#defineBUFFER_SIZE1024intmain(){int sockfd;structsockaddr_in servaddr;int len;char buffer[BUFFER_SIZE]={0};char*hello ="Hello from UDP client";// 创建套接字文件描述符if((sockfd =socket(AF_INET, SOCK_DGRAM,0))<0){perror("socket() error");exit(EXIT_FAILURE);} servaddr.sin_family = AF_INET; servaddr.sin_port =htons(PORT); servaddr.sin_addr.s_addr =inet_addr("127.0.0.1"); len =sizeof(servaddr);// 发送数据sendto(sockfd, hello,strlen(hello),0,(structsockaddr*)&servaddr, len);printf("向服务器发送消息:%s\n", hello);// 接收数据recvfrom(sockfd, buffer, BUFFER_SIZE,0,(structsockaddr*)&servaddr,&len);printf("收到服务器消息:%s\n", buffer);// 关闭套接字close(sockfd);return0;}

五、模块4:实战案例分析——HTTP服务器开发

5.1 学习目标

  • 掌握HTTP服务器开发:使用C语言实现一个简单的HTTP服务器,处理HTTP请求和响应
  • 学会使用套接字编程和HTTP协议实现网络应用
  • 避开实战案例使用的3大常见坑

5.2 HTTP服务器开发

代码示例5:HTTP服务器

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#definePORT8080#defineBUFFER_SIZE4096#defineMAX_CLIENTS10voidhandle_client(int new_socket){char buffer[BUFFER_SIZE]={0};char*response ="HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 12\r\n\r\nHello, World!";// 接收HTTP请求read(new_socket, buffer, BUFFER_SIZE);printf("收到HTTP请求:\n%s\n", buffer);// 发送HTTP响应send(new_socket, response,strlen(response),0);printf("向客户端发送响应:\n%s\n", response);// 关闭套接字close(new_socket);}intmain(){int server_fd, new_socket;structsockaddr_in address;int opt =1;int addrlen =sizeof(address);int clients[MAX_CLIENTS];int max_clients = MAX_CLIENTS;int i, j, valread, sd;int activity, max_sd; fd_set readfds;char buffer[BUFFER_SIZE]={0};// 初始化客户端数组for(i =0; i < max_clients; i++){ clients[i]=0;}// 创建套接字文件描述符if((server_fd =socket(AF_INET, SOCK_STREAM,0))==0){perror("socket() error");exit(EXIT_FAILURE);}// 设置套接字选项if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,&opt,sizeof(opt))){perror("setsockopt() error");exit(EXIT_FAILURE);} address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port =htons(PORT);// 绑定套接字到指定地址和端口if(bind(server_fd,(structsockaddr*)&address,sizeof(address))<0){perror("bind() error");exit(EXIT_FAILURE);}// 监听连接if(listen(server_fd,3)<0){perror("listen() error");exit(EXIT_FAILURE);}printf("HTTP服务器启动成功,监听端口:%d\n", PORT);while(1){// 清空文件描述符集合FD_ZERO(&readfds);// 添加服务器套接字到集合FD_SET(server_fd,&readfds); max_sd = server_fd;// 添加客户端套接字到集合for(i =0; i < max_clients; i++){ sd = clients[i];if(sd >0){FD_SET(sd,&readfds);}if(sd > max_sd){ max_sd = sd;}}// 等待活动 activity =select(max_sd +1,&readfds,NULL,NULL,NULL);if(activity <0){perror("select() error");}// 如果服务器套接字有活动,说明有新连接if(FD_ISSET(server_fd,&readfds)){if((new_socket =accept(server_fd,(structsockaddr*)&address,(socklen_t*)&addrlen))<0){perror("accept() error");exit(EXIT_FAILURE);}// 打印客户端信息printf("新连接:套接字文件描述符=%d,IP=%s,端口=%d\n", new_socket,inet_ntoa(address.sin_addr),ntohs(address.sin_port));// 将新套接字文件描述符添加到数组for(i =0; i < max_clients; i++){if(clients[i]==0){ clients[i]= new_socket;break;}}if(i == max_clients){printf("客户端数量已达上限!\n");}}// 处理客户端活动for(i =0; i < max_clients; i++){ sd = clients[i];if(FD_ISSET(sd,&readfds)){ valread =read(sd, buffer, BUFFER_SIZE);if(valread ==0){// 客户端关闭连接getpeername(sd,(structsockaddr*)&address,(socklen_t*)&addrlen);printf("客户端断开连接:IP=%s,端口=%d\n",inet_ntoa(address.sin_addr),ntohs(address.sin_port));close(sd); clients[i]=0;}else{// 处理HTTP请求 buffer[valread]='\0';handle_client(sd);// 关闭套接字close(sd); clients[i]=0;}}}}return0;}

六、本章总结与课后练习

6.1 总结

TCP/IP协议栈:用于网络通信的协议集合,分为应用层、传输层、网络层、数据链路层
套接字编程:网络通信的端点,用于实现服务器和客户端之间的通信
服务器/客户端通信:服务器监听连接,客户端发起连接,实现数据传输
实战案例分析:HTTP服务器开发,使用套接字编程和HTTP协议实现网络应用

6.2 课后练习

  1. 编写程序:使用TCP协议实现服务器和客户端通信
  2. 编写程序:使用UDP协议实现服务器和客户端通信
  3. 编写程序:使用HTTP协议实现简单的Web服务器
  4. 编写程序:使用FTP协议实现文件传输
  5. 编写程序:使用SMTP协议实现邮件发送
  6. 编写程序:使用POP3协议实现邮件接收
  7. 编写程序:使用DNS协议实现域名解析
  8. 编写程序:使用ICMP协议实现Ping功能
  9. 编写程序:使用ARP协议实现地址解析
  10. 编写程序:使用SSL/TLS协议实现安全通信

Read more

【大模型实战篇】基于Claude MCP协议的智能体落地示例

【大模型实战篇】基于Claude MCP协议的智能体落地示例

1. 背景         之前我们在《MCP(Model Context Protocol) 大模型智能体第一个开源标准协议》一文中,介绍了MCP的概念,虽然了解了其概念、架构、解决的问题,但还缺少具体的示例,来帮助进一步理解整套MCP框架如何落地。         今天我们基于claude的官方例子--获取天气预报【1】,来理解MCP落地的整条链路。 2. MCP示例         该案例是构建一个简单的MCP天气预报服务器,并将其连接到主机,即Claude for Desktop。从基本设置开始,然后逐步发展到更复杂的使用场景。         大模型虽然能力非常强,但其弊端就是内容是过时的,这里的过时不是说内容很旧,只是表达内容具有非实时性。比如没有获取天气预报和严重天气警报的能力。因此我们将使用MCP来解决这一问题。         构建一个服务器,该服务器提供两个工具:获取警报(get-alerts)和获取预报(get-forecast)。然后,将该服务器连接到MCP主机(在本例中为Claude for Desktop)。         首先我们配置下环

By Ne0inhk
AI Agent新范式:FastGPT+MCP协议实现工具增强型智能体构建

AI Agent新范式:FastGPT+MCP协议实现工具增强型智能体构建

AI Agent新范式:FastGPT+MCP协议实现工具增强型智能体构建 作者:高瑞冬 本文目录 * AI Agent新范式:FastGPT+MCP协议实现工具增强型智能体构建 * 一、MCP协议简介 * 二、创建MCP工具集 * 1. 获取MCP服务地址 * 2. 在FastGPT中创建MCP工具集 * 三、测试MCP工具 * 四、AI模型调用MCP工具 * 1. 调用单个工具 * 2. 调用整个工具集 * 五、私有化部署支持 * 1. 环境准备 * 2. 修改docker-compose.yml文件 * 3. 修改FastGPT配置 * 4. 重启服务 * 六、使用MCP-Proxy集成多个MCP服务 * 1. MCP-Proxy简介 * 2. 安装MCP-Proxy * 3. 配置MCP-Proxy * 4. 将MCP-Proxy与FastGPT集成 * 5. 高级配置

By Ne0inhk
AI革命先锋:DeepSeek与蓝耘通义万相2.1的无缝融合引领行业智能化变革

AI革命先锋:DeepSeek与蓝耘通义万相2.1的无缝融合引领行业智能化变革

云边有个稻草人-ZEEKLOG博客 目录 引言 一、什么是DeepSeek? 1.1 DeepSeek平台概述 1.2 DeepSeek的核心功能与技术 二、蓝耘通义万相2.1概述 2.1 蓝耘科技简介 2.2 蓝耘通义万相2.1的功能与优势 1. 全链条智能化解决方案 2. 强大的数据处理能力 3. 高效的模型训练与优化 4. 自动化推理与部署 5. 行业专用解决方案 三、蓝耘通义万相2.1与DeepSeek的对比分析 3.1 核心区别 3.2 结合使用的优势 四、蓝耘注册流程 五、DeepSeek与蓝耘通义万相2.1的集成应用 5.1 集成应用场景 1. 智能医疗诊断

By Ne0inhk
基于腾讯云HAI + DeepSeek快速设计自己的个人网页

基于腾讯云HAI + DeepSeek快速设计自己的个人网页

前言:通过结合腾讯云HAI 强大的云端运算能力与DeepSeek先进的 AI技术,本文介绍高效、便捷且低成本的设计一个自己的个人网页。你将了解到如何轻松绕过常见的技术阻碍,在腾讯云HAI平台上快速部署DeepSeek模型,仅需简单几步,就能获取一个包含个人简介、技能特长、项目经历及联系方式等核心板块的响应式网页。 目录 一、DeepSeek模型部署在腾讯云HAI 二、设计个人网页 一、DeepSeek模型部署在腾讯云HAI 把 DeepSeek 模型部署于腾讯云 HAI,用户便能避开官网访问限制,直接依托腾讯云 HAI 的超强算力运行 DeepSeek-R1 等模型。这一举措不仅降低了技术门槛,还缩短了部署时间,削减了成本。尤为关键的是,凭借 HAI 平台灵活且可扩展的特性,用户能够依据自身特定需求定制专属解决方案,进而更出色地适配特定业务场景,满足各类技术要求 。 点击访问腾讯云HAI控制台地址: 算力管理 - 高性能应用服务 - 控制台 腾讯云高性能应用服务HAI已支持DeepSeek-R1模型预装环境和CPU算力,只需简单的几步就能调用DeepSeek - R1

By Ne0inhk