客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFFER_SIZE 1024
// 全局变量声明
int sock = 0;
char buffer[BUFFER_SIZE] = { 0 };
int server_disconnected = 0;
// 函数原型声明
void* receive_messages(void* arg); // 接收消息线程函数
void send_file(const char* file_path); // 发送文件函数
int main(int argc, char* argv[])
{
struct sockaddr_in serv_addr;
if (argc != 3)
{
printf("Usage: %s <IP> <PORT>\n", argv[0]);
return -1;
}
// 创建 TCP 套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Socket creation error");
return -1;
}
serv_addr.sin_family = AF_INET; // 使用 IPv4 协议
serv_addr.sin_port = htons(atoi(argv[2])); // 端口号
// 将 IPv4 地址从文本转换为网络字节顺序
if (inet_pton(AF_INET, argv[1], &serv_addr.sin_addr) <= 0)
{
perror("Invalid address/ Address not supported");
close(sock);
return -1;
}
// 连接到服务器
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
perror("Connection failed");
close(sock);
return -1;
}
// 创建接收消息的线程
pthread_t recv_thread;
if (pthread_create(&recv_thread, NULL, receive_messages, NULL) != 0)
{
perror("Failed to create thread");
close(sock);
return -1;
}
// 主线程负责发送数据
while (!server_disconnected)
{
printf("请选择:\n");
printf("1. 发送消息\n");
printf("2. 发送文件\n");
printf("3. 退出程序\n");
printf("请输入选项: ");
// 使用 fgets 获取输入
char choice_str[10] = { 0 };
if (fgets(choice_str, sizeof(choice_str), stdin) == NULL)
{
perror("Failed to read input");
continue;
}
int choice = atoi(choice_str);
if (server_disconnected) break;
switch (choice)
{
case 1:
while (1)
{
printf("请输入消息(输入exit回到菜单): ");
if (fgets(buffer, BUFFER_SIZE, stdin) == NULL)
{
perror("Failed to read message");
break;
}
if (strcmp(buffer, "exit\n") == 0)
{
break;
}
if (send(sock, buffer, strlen(buffer), 0) <= 0)
{
perror("Failed to send message");
break;
}
}
break;
case 2:
printf("请输入要发送的文件路径: ");
char file_path[BUFFER_SIZE] = { 0 };
if (fgets(file_path, BUFFER_SIZE, stdin) == NULL)
{
perror("Failed to read file path");
break;
}
// 去除换行符
file_path[strcspn(file_path, "\n")] = 0;
send_file(file_path);
break;
case 3:
// 退出程序
printf("退出程序...\n");
close(sock);
pthread_cancel(recv_thread); // 取消接收消息的线程
pthread_join(recv_thread, NULL); // 等待线程结束
return 0;
default:
printf("输入错误,请重新输入!\n");
break;
}
}
close(sock);
return 0;
}
void* receive_messages(void* arg)
{
while (1)
{
memset(buffer, 0, BUFFER_SIZE);
int valread = recv(sock, buffer, BUFFER_SIZE, 0);
if (valread > 0)
{
printf("Server: %s", buffer);
}
else if (valread == 0)
{
printf("Server disconnected.\n");
server_disconnected = 1;
break;
}
else
{
perror("Receive failed");
server_disconnected = 1;
break;
}
}
pthread_exit(NULL);
}
void send_file(const char* file_path)
{
FILE* file = fopen(file_path, "rb");
if (file == NULL)
{
perror("Failed to open file");
return;
}
// 发送文件名
const char* file_name = strrchr(file_path, '/');
if (file_name == NULL) {
file_name = file_path;
}
else {
file_name++; // 去掉 '/'
}
// 发送文件名给服务器
if (send(sock, file_name, strlen(file_name), 0) == -1)
{
perror("Failed to send file name");
fclose(file);
return;
}
// 发送文件内容
while (1)
{
unsigned char file_buffer[BUFFER_SIZE] = { 0 };
size_t bytes_read = fread(file_buffer, 1, BUFFER_SIZE, file);
if (bytes_read > 0)
{
if (send(sock, file_buffer, bytes_read, 0) == -1)
{
perror("Failed to send file data");
break;
}
}
if (bytes_read < BUFFER_SIZE)
{
if (feof(file))
{
printf("File transfer completed.\n");
}
if (ferror(file))
{
perror("Error reading from file");
}
break;
}
}
fclose(file);
}
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFFER_SIZE 1024
// 线程处理客户端连接
void* client_handler(void* arg); // 普通消息处理线程
void* file_receive_handler(void* arg); // 文件接收线程
int main(char argc, char* argv[])
{
if(argc!= 2)
{
printf("格式错误,请指定端口号\n");
printf("Usage: %s <port>\n", argv[0]);
exit(EXIT_FAILURE);
}
int server_fd;
struct sockaddr_in address;
int addrlen = sizeof(address);
pthread_t thread_id;
// 创建 TCP 套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(atoi(argv[1]));
// 绑定套接字到指定地址和端口
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0)
{
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 开始监听连接请求
if (listen(server_fd, 3) < 0)
{
perror("listen");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Server listening on port %d\n", atoi(argv[1]));
// 接受连接
while (1)
{
int* new_socket_ptr = malloc(sizeof(int));
if (new_socket_ptr == NULL)
{
perror("malloc failed");
continue;
}
*new_socket_ptr = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
if (*new_socket_ptr < 0)
{
perror("accept");
free(new_socket_ptr);
continue;
}
// 创建线程处理客户端连接
if (pthread_create(&thread_id, NULL, client_handler, (void*)new_socket_ptr) != 0)
{
perror("pthread_create failed");
close(*new_socket_ptr);
free(new_socket_ptr);
}
else
{
pthread_detach(thread_id); // 线程分离,自动清理资源
}
}
close(server_fd);
return 0;
}
// 普通消息处理线程
void* client_handler(void* arg)
{
int new_socket = *((int*)arg);
char buffer[BUFFER_SIZE] = { 0 };
const char* response = "Echo: ";
while (1)
{
int valread = read(new_socket, buffer, BUFFER_SIZE);
if (valread <= 0)
{
// 连接关闭或读取错误
break;
}
buffer[valread] = '\0'; // 确保缓冲区以 null 终止
printf("Received: %s\n", buffer);
// 如果消息是文件传输请求,则启动文件接收线程
if (strncmp(buffer, "FILE:", 5) == 0)
{
pthread_t file_thread_id;
char* filename = strdup(buffer + 5); // 提取文件名
if (pthread_create(&file_thread_id, NULL, file_receive_handler, (void*)filename) != 0)
{
perror("pthread_create (file_receive_handler) failed");
free(filename);
}
else
{
pthread_detach(file_thread_id); // 文件接收线程分离
}
}
else
{
// 发送响应
send(new_socket, response, strlen(response), 0);
send(new_socket, buffer, strlen(buffer), 0);
}
memset(buffer, 0, BUFFER_SIZE);
}
close(new_socket);
free(arg); // 释放分配的内存
pthread_exit(NULL);
}
// 文件接收线程
void* file_receive_handler(void* arg)
{
}