服务器代码:
#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
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
printf("=== TCP服务器启动 ===\n");
1. 创建Socket文件描述符
//AF_INET = IPv4, SOCK_STREAM = TCP, 0 = 默认协议
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket创建失败");
exit(EXIT_FAILURE);
}
printf("1. Socket创建成功\n");
2. 设置服务器地址
address.sin_family = AF_INET; // IPv4
address.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
address.sin_port = htons(PORT); // 端口号,htons转换字节序
3. 绑定Socket到地址
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("绑定失败");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("2. 绑定端口 %d 成功\n", PORT);
4. 开始监听连接
5 等待连接队列的最大长度
if (listen(server_fd, 5) < 0) {
perror("监听失败");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("3. 开始监听,等待客户端连接...\n");
5. 接受客户端连接没人连接则阻塞
client_fd = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
if (client_fd < 0) {
perror("接受连接失败");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("4. 客户端连接成功!\n");
6. 接收客户端数据 会阻塞
int bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0'; // 添加字符串结束符
printf("5. 收到客户端消息: %s\n", buffer);
}
7. 发送回复给客户端
char* response = "你好客户端!我是服务器!";
write(client_fd, response, strlen(response));
printf("6. 已发送回复给客户端\n");
8. 关闭连接
close(client_fd);
close(server_fd);
printf("7. 连接关闭,服务器退出\n");
return 0;
}
端口号
0~1024知名端口
1025~4096 保留端口
一般用大于4096的
客户端代码
#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 SERVER_IP "127.0.0.1" // 本地回环地址
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sockfd;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
printf("=== TCP客户端启动 ===\n");
// 1. 创建Socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket创建失败");
exit(EXIT_FAILURE);
}
printf("1. Socket创建成功\n");
// 2. 设置服务器地址
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 将IP地址字符串转换为网络字节序
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
perror("IP地址转换失败");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("2. 服务器地址设置: %s:%d\n", SERVER_IP, PORT);
// 3. 连接服务器
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("连接服务器失败");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("3. 连接服务器成功!\n");
// 4. 发送数据到服务器
char* message = "你好服务器!我是客户端!";
write(sockfd, message, strlen(message));
printf("4. 已发送消息: %s\n", message);
// 5. 接收服务器回复
int bytes_read = read(sockfd, buffer, BUFFER_SIZE - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("5. 收到服务器回复: %s\n", buffer);
}
// 6. 关闭连接
close(sockfd);
printf("6. 连接关闭,客户端退出\n");
return 0;
}
多进程并发
多进程并发让服务器可以同时服务多个客户端。
多进程服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/wait.h>
int main() {
printf("=== 多进程服务器启动 ===\n");
// 1. 创建"总机电话"
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 设置电话号码
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
// 3. 绑定电话号码
bind(server_fd, (struct sockaddr*)&address, sizeof(address));
// 4. 开始等待来电
listen(server_fd, 5);
printf("服务器准备就绪,等待客户连接...\n");
while (1) { // 无限循环,持续服务
printf("\n=== 等待新客户 ===\n");
// 5. 接听电话(接待顾客)
int client_fd = accept(server_fd, NULL, NULL);
printf("有新客户连接!\n");
// 6. 神奇的一步:创建子进程(分配服务员)
pid_t pid = fork();
if (pid == 0) {
// 子进程区域(服务员的工作)
printf("子进程 %d: 开始服务客户\n", getpid());
// 关闭不需要的服务器socket(子进程不需要接听新电话)
close(server_fd);
// 与客户对话
char buffer[1024] = {0};
read(client_fd, buffer, sizeof(buffer));
printf("子进程 %d: 收到客户消息: %s\n", getpid(), buffer);
// 回复客户
char reply[100];
snprintf(reply, sizeof(reply), "你好!我是服务员%d", getpid());
write(client_fd, reply, strlen(reply));
printf("子进程 %d: 服务完成,退出\n", getpid());
close(client_fd);
exit(0); // 子进程完成任务后退出
}
else {
// 父进程区域(接待员的工作)
printf("父进程: 已分配子进程 %d 服务客户\n", pid);
close(client_fd); // 父进程不需要与客户直接通话
}
}
close(server_fd);
return 0;
}
=== 多进程服务器启动 ===
服务器准备就绪,等待客户连接...
=== 等待新客户 ===
有新客户连接!
父进程: 已分配子进程 1234 服务客户
子进程 1234: 开始服务客户
子进程 1234: 收到客户消息: 你好服务器!我是客户5678
子进程 1234: 服务完成,退出
=== 等待新客户 ===
有新客户连接!
父进程: 已分配子进程 1235 服务客户
子进程 1235: 开始服务客户
子进程 1235: 收到客户消息: 你好服务器!我是客户5679
子进程 1235: 服务完成,退出
测试客户端(可以开多个终端测试)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
printf("=== 客户端启动 ===\n");
// 1. 创建电话
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 拨打服务器电话
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
printf("连接到服务器成功!\n");
// 3. 发送消息
char message[100];
snprintf(message, sizeof(message), "你好服务器!我是客户%d", getpid());
write(sockfd, message, strlen(message));
printf("发送: %s\n", message);
// 4. 接收回复
char buffer[1024] = {0};
read(sockfd, buffer, sizeof(buffer));
printf("收到回复: %s\n", buffer);
// 5. 保持连接一段时间,方便观察
printf("等待5秒后退出...\n");
sleep(5);
close(sockfd);
printf("客户端退出\n");
return 0;
}
=== 客户端启动 ===
连接到服务器成功!
发送: 你好服务器!我是客户5678
收到回复: 你好!我是服务员1234
等待5秒后退出...
客户端退出
多线程并发
简单的多线程并发服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
// 线程参数结构
typedef struct {
int client_fd;
} thread_arg_t;
// 线程处理函数
void* handle_client(void* arg) {
thread_arg_t* targ = (thread_arg_t*)arg;
int client_fd = targ->client_fd;
// 处理客户端请求
char buffer[100];
read(client_fd, buffer, sizeof(buffer));
printf("线程%lu: 收到: %s\n", pthread_self(), buffer);
// 回复客户端
char reply[100];
snprintf(reply, sizeof(reply), "Hello from thread %lu", pthread_self());
write(client_fd, reply, strlen(reply));
close(client_fd);
printf("线程%lu: 服务完成\n", pthread_self());
free(targ); // 释放线程参数内存
return NULL;
}
int main() {
printf("=== 多线程服务器 (端口: 8081) ===\n");
// 创建服务器socket
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
// 设置地址
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8081);
bind(server_fd, (struct sockaddr*)&address, sizeof(address));
listen(server_fd, 5);
printf("等待客户端连接...\n");
while (1) {
// 接受客户端连接
int client_fd = accept(server_fd, NULL, NULL);
printf("新客户端连接\n");
// 创建线程参数
thread_arg_t* targ = malloc(sizeof(thread_arg_t));
targ->client_fd = client_fd;
// 创建新线程处理客户端
pthread_t thread_id;
pthread_create(&thread_id, NULL, handle_client, targ);
pthread_detach(thread_id); // 线程分离,自动回收资源
}
close(server_fd);
return 0;
}
=== 多线程服务器 (端口: 8081) ===
等待客户端连接...
新客户端连接
线程140123456: 收到: Hello from client 5678
线程140123456: 服务完成
新客户端连接
线程140123789: 收到: Hello from client 5679
线程140123789: 服务完成
测试客户端(通用版)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("用法: %s <IP> <端口>\n", argv[0]);
printf("示例: %s 127.0.0.1 8080\n", argv[0]);
return 1;
}
printf("=== 客户端启动 ===\n");
// 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 设置服务器地址
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET, argv[1], &serv_addr.sin_addr);
// 连接服务器
connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
printf("连接到服务器 %s:%s 成功!\n", argv[1], argv[2]);
// 发送消息
char message[100];
snprintf(message, sizeof(message), "Hello from client %d", getpid());
write(sockfd, message, strlen(message));
printf("发送: %s\n", message);
// 接收回复
char buffer[100] = {0};
read(sockfd, buffer, sizeof(buffer));
printf("收到: %s\n", buffer);
// 保持连接一段时间便于观察
sleep(2);
close(sockfd);
printf("客户端退出\n");
return 0;
}
特性
|
多进程
|
多线程
|
创建方式
|
fork()
|
pthread_create()
|
资源开销
|
大(独立内存空间)
|
小(共享内存空间)
|
数据共享
|
困难(需要IPC)
|
容易(共享全局变量)
|
稳定性
|
高(进程崩溃不影响其他)
|
低(线程崩溃影响整个进程)
|
通信成本
|
高(进程间通信复杂)
|
低(直接访问共享内存)
|
创建速度
|
慢
|
快
|
- 用多进程当:需要高稳定性,任务相互独立
- 用多线程当:需要高性能,任务间需要频繁通信