USER
手写服务器与手写客户端相连接
ASSISTANT
好的,我们可以使用 C 语言和 POSIX Sockets API 来实现一个基础的 TCP 服务器和客户端。这个示例将展示它们如何建立连接、发送和接收消息。
核心思路:
- 服务器 (Server):
- 创建一个监听套接字 (socket)。
- 将该套接字绑定 (bind) 到一个特定的 IP 地址和端口。
- 开始监听 (listen) 该端口上的连接请求。
- 接受 (accept) 一个客户端的连接,这会创建一个新的套接字用于与该客户端通信。
- 通过这个新套接字与客户端交换数据 (recv,send)。
- 关闭套接字 (close)。
- 客户端 (Client):
- 创建一个套接字 (socket)。
- 连接到服务器的 IP 地址和端口 (connect)。
- 通过套接字与服务器交换数据 (send,recv)。
- 关闭套接字 (close)。
注意事项:
- 本示例使用localhost(127.0.0.1) 作为通信地址,适合在本机上测试。
- 代码中包含了基本的错误检查。
- 服务器在处理完一个客户端后会退出。若要处理多个客户端,服务器需要在accept后创建新线程或进程。
- 编译时需要链接网络库(在 Linux/macOS 上通常不需要特殊标志,但在 Windows 上需要链接ws2_32库)。
服务器代码 (server.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // For close(), read(), write()
#include <sys/socket.h> // For socket functions
#include <netinet/in.h> // For sockaddr_in
#include <arpa/inet.h> // For inet_addr
#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};
const char *hello = "Hello from server";
// 1. 创建服务器套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 2. 设置套接字选项 (可选,例如允许地址重用)
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
// 3. 定义服务器地址结构
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 监听本地地址
address.sin_port = htons(PORT); // 监听端口
// 4. 将套接字绑定到地址
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 5. 开始监听
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// 6. 接受客户端连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
printf("Client connected.\n");
// 7. 与客户端通信
int valread = read(new_socket, buffer, BUFFER_SIZE);
if (valread > 0) {
printf("Client said: %s\n", buffer);
}
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent to client\n");
// 8. 清理
close(new_socket);
close(server_fd);
return 0;
}
客户端代码 (client.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // For close(), read(), write()
#include <sys/socket.h> // For socket functions
#include <netinet/in.h> // For sockaddr_in
#include <arpa/inet.h> // For inet_addr
#define PORT 8080
#define IP "127.0.0.1" // 服务器IP地址
#define BUFFER_SIZE 1024
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[BUFFER_SIZE] = {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);
// 2. 将 IP 地址从字符串转换为网络字节序
if(inet_pton(AF_INET, IP, &serv_addr.sin_addr)<=0) {
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// 3. 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
printf("Connected to server.\n");
// 4. 与服务器通信
send(sock, hello, strlen(hello), 0);
printf("Hello message sent to server\n");
int valread = read(sock, buffer, BUFFER_SIZE);
if (valread > 0) {
printf("Server said: %s\n", buffer);
}
// 5. 清理
close(sock);
return 0;
}
编译和运行
- 编译:
gcc -o server server.c
gcc -o client client.c
- 运行:
- 首先,在一个终端窗口中启动服务器:./server
你会看到 "Server listening on port 8080..." 的提示。
-
- 然后,在另一个终端窗口中运行客户端:./client
- 回到服务器窗口,你应该能看到 "Client connected." 和 "Client said: Hello from client"。
- 回到客户端窗口,你应该能看到 "Connected to server." 和 "Server said: Hello from server"。
这样,手写的 C 语言服务器和客户端就成功连接并交换了消息。