C 语言 tcp 编程
外观
C语言TCP编程是网络编程中的核心内容,它基于传输控制协议(TCP)实现可靠的双向通信。本文将从基础概念到实际应用,逐步讲解如何使用C语言进行TCP编程。
概述[编辑 | 编辑源代码]
TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层协议。在C语言中,TCP编程主要通过套接字(socket)接口实现,涉及以下关键步骤:
- 创建套接字
- 绑定地址和端口
- 监听连接(服务端)
- 发起连接(客户端)
- 数据传输
- 关闭连接
基础概念[编辑 | 编辑源代码]
TCP协议特性[编辑 | 编辑源代码]
- 可靠性:通过确认机制、重传机制和校验和保证数据正确传输。
- 面向连接:通信前需建立连接(三次握手),结束时需断开连接(四次挥手)。
- 全双工通信:双方可同时发送和接收数据。
套接字类型[编辑 | 编辑源代码]
C语言中常用的TCP套接字类型为:
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET表示IPv4,SOCK_STREAM表示TCP
编程模型[编辑 | 编辑源代码]
服务端流程[编辑 | 编辑源代码]
1. 创建套接字 2. 绑定地址(bind) 3. 监听连接(listen) 4. 接受连接(accept) 5. 数据交换(send/recv) 6. 关闭连接
客户端流程[编辑 | 编辑源代码]
1. 创建套接字 2. 连接服务器(connect) 3. 数据交换(send/recv) 4. 关闭连接
代码实现[编辑 | 编辑源代码]
基本TCP服务端[编辑 | 编辑源代码]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
// 1. 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 2. 绑定地址
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 3. 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// 4. 接受连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 5. 数据交换
read(new_socket, buffer, BUFFER_SIZE);
printf("Client says: %s\n", buffer);
send(new_socket, "Hello from server", strlen("Hello from server"), 0);
// 6. 关闭连接
close(new_socket);
close(server_fd);
return 0;
}
基本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>
#define PORT 8080
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[1024] = {0};
// 1. 创建套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 转换IP地址
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// 2. 连接服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
// 3. 数据交换
send(sock, hello, strlen(hello), 0);
printf("Hello message sent\n");
read(sock, buffer, 1024);
printf("Server says: %s\n", buffer);
// 4. 关闭连接
close(sock);
return 0;
}
关键函数详解[编辑 | 编辑源代码]
socket()[编辑 | 编辑源代码]
创建通信端点,返回文件描述符。
- 参数:
- domain:地址族(如AF_INET)
- type:套接字类型(如SOCK_STREAM)
- protocol:通常为0(自动选择)
bind()[编辑 | 编辑源代码]
将套接字与特定IP地址和端口号绑定。
- 参数:
- sockfd:套接字描述符
- addr:指向包含地址信息的结构体
- addrlen:地址结构体长度
listen()[编辑 | 编辑源代码]
使套接字进入被动监听状态。
- 参数:
- sockfd:套接字描述符
- backlog:等待连接队列的最大长度
accept()[编辑 | 编辑源代码]
接受连接请求,返回新套接字描述符。
- 参数:
- sockfd:监听套接字描述符
- addr:客户端地址信息
- addrlen:地址结构体长度
高级主题[编辑 | 编辑源代码]
多客户端处理[编辑 | 编辑源代码]
使用多进程或多线程处理多个客户端连接:
// 在accept循环中
while (1) {
int new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
if (fork() == 0) { // 子进程处理连接
close(server_fd);
// 处理客户端请求
handle_client(new_socket);
exit(0);
}
close(new_socket);
}
非阻塞I/O[编辑 | 编辑源代码]
使用fcntl设置非阻塞模式:
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
实际应用案例[编辑 | 编辑源代码]
简单聊天程序[编辑 | 编辑源代码]
实现一个基本的客户端-服务器聊天系统: 1. 服务器启动并等待连接 2. 客户端连接后可以发送消息 3. 服务器接收并回复消息 4. 双方可以持续通信直到输入"exit"
文件传输[编辑 | 编辑源代码]
通过TCP实现文件传输: 1. 客户端发送文件名 2. 服务器确认文件存在 3. 分块传输文件数据 4. 校验文件完整性
常见问题[编辑 | 编辑源代码]
地址已在使用[编辑 | 编辑源代码]
解决方法:
- 设置SO_REUSEADDR套接字选项
- 等待TCP TIME_WAIT状态结束(通常2MSL时间)
连接重置[编辑 | 编辑源代码]
可能原因:
- 对端意外关闭连接
- 网络问题
- 协议不匹配
性能优化[编辑 | 编辑源代码]
- 使用select/poll/epoll处理多连接
- 设置合适的缓冲区大小
- 启用TCP_NODELAY选项减少延迟
- 使用SO_KEEPALIVE检测连接状态
安全考虑[编辑 | 编辑源代码]
- 验证输入数据防止缓冲区溢出
- 使用SSL/TLS加密敏感数据
- 限制并发连接数防止DoS攻击
- 正确处理错误和异常情况
总结[编辑 | 编辑源代码]
C语言TCP编程是构建网络应用的基础,通过套接字API可以实现可靠的通信。掌握TCP编程需要理解协议特性、熟悉相关系统调用,并注意性能和安全性问题。实际开发中,通常会结合多线程、I/O多路复用等技术构建更复杂的网络应用。