2024-02-29-Linux高级网络编程(4-TCP编程)
1.2024-02-16-物联网C语言(1-数据类型与语句)2.2024-02-17-物联网C语言(2-数组)3.2024-02-17-物联网C语言(3-函数)4.2024-02-17-物联网C语言(4-预处理)5.2024-02-17-物联网C语言(5-指针)6.2024-02-18-物联网C语言(6-动态内存申请)7.2024-02-18-物联网C语言(7-字符串处理函数)8.2024-02-18-物联网C语言(8-结构体、共用体、枚举)9.2024-02-19-物联网C语言(9-链表)10.2024-02-20-物联网C语言(10-文件)11.2024-02-21-物联网系统编程(1-Shell语言)12.2024-02-21-物联网系统编程(2-系统调用)13.2024-02-22-物联网系统编程(3-进程)14.2024-02-23-物联网系统编程(4-信号)15.2024-02-24-物联网系统编程(5-管道、命名管道)16.2024-02-27-物联网系统编程(6-消息队列)17.2024-02-27-物联网系统编程(7- 共享内存)18.2024-02-27-物联网系统编程(8-线程)19.2024-02-28-物联网系统编程(9-多任务互斥与同步)20.2024-02-29-Linux高级网络编程(1-计算机网络概述)21.2024-02-29-Linux高级网络编程(2-UDP编程)22.2024-02-29-Linux高级网络编程(3-UDP编程-TFTP、广播、多播)
23.2024-02-29-Linux高级网络编程(4-TCP编程)
24.2024-03-01-Lniux高级网络编程(5-网络通信过程)25.2024-03-01-Linux高级网络编程(6-原始套接字)4. TCP编程
4.1 TCP介绍
- 面向连接的流式协议;可靠、出错重传、且每收到一个数据都要给出相应的确认
- 通信之前需要建立链接
- 服务器被动链接,客户端是主动链接

TCP编程流程
服务器: 1. 创建套接字 socket() 2. 将套接字与服务器网络信息结构体绑定 bind() 3. 将套接字设置为监听状态 listen() 4. 阻塞等待客户端的连接请求 accept()进行通信 recv()/send() 5. 关闭套接字 close() 客户端: 1. 创建套接字 socket() 2. 发送客户端连接请求 connect() 3. 进行通信 send()/recv() 4. 关闭套接字 close()
4.2 TCP编程-socket
#include<sys/types.h> #include<sys/socket.h> int socket(int domain,int type, int protocol); 功能:创建一个套接字,返回一个文件描述符; 参数: domain:通信域,协议族 AF_UNIX 本地通信 AF_INET ipv4网络协议 AF_INET6 ipv6网络协议 AF_PACKET 底层接口 type:套接字的类型 SOCK_STREAM 流式套接字(tcp) SOCK_DGRAM 数据报套接字(udp) SOCK_RAW 原始套接字(用于链路层) protocol: 附加协议,如果不需要,则设置为0 返回值: 成功:文件描述符 失败:-1
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> int main(int argc, char const *argv[]) { int socket_id; if ((socket_id = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("FAIL TO CREAET TCP SOCKET"); exit(1); } printf("socket_id = %d \n", socket_id); return 0; }
输出结果
socket_id = 3
4.3 TCP客户端 -connect、send、recv
4.3.1 connect函数
#include<sys/types.h> #include<sys/socket.h> int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 功能:给服务器发送客户端的连接请求; 参数: sockfd:文件描述符,socket函数的返回值 addr:要连接的服务器的网络信息结构体(需要自己设置); addrlen:add的长度返回值: 成功:0 失败:-1
4.3.2 send函数
#include <sys/types.h> #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags); 功能:发送数据 参数: sockfd:文件描述符 客户端:socket函数的返回值; 服务器:accept函数的返回值; buf:发送的数据 len:buf的长度 flags:标志位 0 阻塞 MSG DONTWAIT 非阻塞 返回值: 成功:发送的字节数 失败:-1
4.3.3 recv函数
#include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t len, int flags); 功能:接收数据 参数: sockfd:文件描述符 客户端:socket函数的返回值 服务器:accept函数的返回值 buf:保存接收到的数据 len:buf的长度 flags:标志位 0 阻塞。 MSG DONTWAIT 非阻塞 返回值: 成功:接收的字节数 失败:-1 注意:如果发送端关闭文件描述符或者关闭进程,则recv函数会返回0
4.4 TCP服务端- bind、listen、accept
4.4.1 bind函数
#include<sys/types.h> #include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 功能:将套接字与网络信息结构体绑定; 参数: sockfd:文件描述符,socket的返回值; addr:网络信息结构体,通用结构体(一般不用)struct sockaddr 网络信息结构体 sockaddr_in #include <netinet/in.h> struct sockaddr_in addrlen:addr的长度 返回值: 成功:0 失败:-1
4.4.2 listen函数
#include <sys/socket.h> int listen(intsockfd, int backlog) 功能: 将套接字由主动修改为被动 使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接; 参数: sockfd: socket监听套接字 backlog: 连接队列的长度,即同一时间允许客户端连接的个数; 返回值: 成功:返回 0 失败:其他
4.4.3 accept函数
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 功能: 阻塞等待客户端的连接请求 参数: sockfd:文件描述符,socket函数的返回值; addr:接收到的客户端的信息结构体(自动填充,定义变量即可); addrlen:addr的长度 返回值: 成功:新的文件描述符(只要有客户端连接,就会产生新的文件描述符; 这个新的文件描述符专门与指定的客户端进行通信的) 失败:-1
4.5 close 函数、三次握手、四次挥手
4.5.1 close函数
- 使用 close 函数即可关闭套接字
关闭一个代表已连接套接字将导致另一端接收到一个0长度的数据包 - 做服务器时
- 关闭监听套接字将导致服务器无法接收新的连接,但不会影响已经建立的连接
- 关闭 accept返回的已连接套接字将导致它所代表的连接被关闭,但不会影响服务器的监听
- 做客户端时
关闭连接就是关闭连接,不意味着其他
4.5.2 三次握手

4.5.3 四次挥手

4.6 TCP并发服务器
TCP原本不是并发服务器,TCP服务器同一时间只能与一个客户端进行通信。
TCP不能实现并发的原因:TCP服务端有两个阻塞函数accept和recv
,所以导致运行一个函数的时候,另一个函数无法执行;这样使得TCP无法保证一边连接客户端,一边与其他客户端通信。
那么,如何实现TCP并发服务器呢?
- 使用多进程实现TCP并发服务器
- 使用多线程实现TCP并发服务器
4.6.1 多进程实现并发
02_mulit_process_tcp_server.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #include <sys/wait.h> #define MAX_BUFFER_SIZE 1024 #define PORT 8080 #define MAX_CONNECTIONS 5 void handle_client(int client_socket) { char buffer[MAX_BUFFER_SIZE]; ssize_t num_bytes; while ((num_bytes = recv(client_socket, buffer, MAX_BUFFER_SIZE, 0)) > 0) { buffer[num_bytes] = '\0'; printf("Received message: %s\n", buffer); // 处理客户端请求,这里简单地将收到的消息原样发送回去 send(client_socket, buffer, num_bytes, 0); } if (num_bytes == 0) { printf("Client disconnected\n"); } else { perror("recv"); } close(client_socket); } int main() { int server_socket, client_socket; struct sockaddr_in server_address, client_address; socklen_t client_address_size; pid_t child_pid; int status; // 创建套接字 server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { perror("socket"); exit(EXIT_FAILURE); } // 设置服务器地址和端口 server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = INADDR_ANY; server_address.sin_port = htons(PORT); // 绑定套接字到指定地址和端口 if (bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) { perror("bind"); exit(EXIT_FAILURE); } // 监听连接请求 if (listen(server_socket, MAX_CONNECTIONS) == -1) { perror("listen"); exit(EXIT_FAILURE); } printf("Server listening on port %d\n", PORT); while (1) { // 接受客户端连接请求 client_address_size = sizeof(client_address); client_socket = accept(server_socket, (struct sockaddr *)&client_address, &client_address_size); if (client_socket == -1) { perror("accept"); exit(EXIT_FAILURE); } // 创建子进程来处理客户端请求 child_pid = fork(); if (child_pid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (child_pid == 0) { // 子进程 close(server_socket); handle_client(client_socket); exit(EXIT_SUCCESS); } else { // 父进程 close(client_socket); // 回收子进程资源,避免僵尸进程 waitpid(-1, &status, WNOHANG); } } // 关闭服务器套接字 close(server_socket); return 0; }
03_multi_process_tcp_client.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #define MAX_BUFFER_SIZE 1024 #define SERVER_IP "127.0.0.1" #define PORT 8080 int main() { int client_socket; struct sockaddr_in server_address; char buffer[MAX_BUFFER_SIZE]; ssize_t num_bytes; // 创建套接字 client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket == -1) { perror("socket"); exit(EXIT_FAILURE); } // 设置服务器地址和端口 server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = inet_addr(SERVER_IP); server_address.sin_port = htons(PORT); // 连接到服务器 if (connect(client_socket, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) { perror("connect"); exit(EXIT_FAILURE); } printf("Connected to server\n"); while (1) { printf("Enter a message (or 'q' to quit): "); fgets(buffer, MAX_BUFFER_SIZE, stdin); // 移除换行符 buffer[strcspn(buffer, "\n")] = '\0'; // 检查是否退出客户端 if (strcmp(buffer, "q") == 0) { break; } // 发送消息给服务器 if (send(client_socket, buffer, strlen(buffer), 0) == -1) { perror("send"); exit(EXIT_FAILURE); } // 接收服务器的响应 num_bytes = recv(client_socket, buffer, MAX_BUFFER_SIZE - 1, 0); if (num_bytes == -1) { perror("recv"); exit(EXIT_FAILURE); } else if (num_bytes == 0) { printf("Server disconnected\n"); break; } buffer[num_bytes] = '\0'; printf("Server response: %s\n", buffer); } // 关闭套接字 close(client_socket); return 0; }
4.6.2 多线程实现并发
void * thread_fun(){ } scoketfd = socket(); bind(); listen(); while(1){ accept(); //只要有客户端,就创建子线程与之通信 pthread_create(,,thread_fun,); pthread_detach(); }
4.7 web服务器介绍
web服务器又称为www服务器、网站服务器等
4.7.1 特点
- 使用HTTP协议与客户机浏览器进行交流
- 不仅能够存储信息,还支持用户通过web服务器运行脚本和程序
- 服务器一般部署在UNIX、Linux或者window等操作系统上,
4.7.2 HTTP协议
概念:一种详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议
特点:
- 支持C/S架构
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径,常用方法:GET、POST
- 无连接: 限制每次连接只处理一个请求
- 无状态: 即如果后续处理需要前面的信息,它必须重传,这样可能导致每次连接传送的数据量会增大
4.7.3 Web编程开发

本文来自博客园,作者:Yasuo_Hasaki,转载请注明原文链接:https://www.cnblogs.com/hasaki-yasuo/p/18046267
合集:
物联网
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步