Socket 服务器技术文档
1. 概述
本文档描述了一个基于 C++ 的简单 TCP socket 服务器的实现和用法。该服务器监听指定的端口,接受客户端连接,读取客户端发送的消息,并向客户端发送一个固定的响应消息。
2. 环境要求
- C++ 编译器,支持 C++11 或更高版本
- POSIX 兼容操作系统
- 支持 POSIX socket API 的库
3. 编译和运行
6. 后续改进
4. 服务器实现
4.1 头文件
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
这些头文件分别用于输入输出流、字符串操作、socket 编程、网络地址结构和 UNIX 标准函数。
4.2 定义端口号和缓冲区大小
#define PORT 8080
#define BUFFER_SIZE 1024
PORT 定义了服务器监听的端口号,BUFFER_SIZE 定义了用于读取和发送数据的缓冲区大小。
4.3 主函数
int main() {
// …
}
主函数是程序的入口点,包含了服务器的主要逻辑。
4.4 创建 socket
int server_fd, new_socket;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
创建一个 socket 文件描述符 server_fd,用于监听客户端连接。AF_INET 表示 IPv4 地址族,SOCK_STREAM 表示 TCP 流套接字。
4.5 绑定地址和端口
struct sockaddr_in address;
bind(server_fd, (struct sockaddr*)&address, sizeof(address));
将 socket 绑定到本地地址和端口。
4.6 监听连接
listen(server_fd, 3);
开始监听是否有客户端连接。第二个参数 3 表示最大连接数。
4.7 接受连接
new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
接受客户端的连接,并返回一个新的 socket 文件描述符 new_socket。
4.8 读取和发送数据
read(new_socket, buffer, BUFFER_SIZE);
send(new_socket, hello, strlen(hello), 0);
从客户端读取数据到缓冲区,并向客户端发送固定的响应消息。
4.9 关闭 socket
close(server_fd);
关闭服务器 socket,释放资源。
5. 注意事项
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.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};
const char* hello = "Hello from server";
// 创建 socket 文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 绑定 socket 到地址和端口
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address))<0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听是否有客户端连接
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 接受客户端的连接
if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen))<0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 读取数据
read(new_socket, buffer, BUFFER_SIZE);
std::cout << "Message from client: " << buffer << std::endl;
// 发送数据
send(new_socket, hello, strlen(hello), 0);
std::cout << "Hello message sent\\n";
// 关闭 socket
close(server_fd);
return 0;
}
几个问题1.sockfd是什么?答:
在 socket 编程中,sockfd 是一个术语,通常用于指代一个 socket 的文件描述符。在 Unix-like 系统中,几乎所有的 I/O 操作都是通过文件描述符进行的,包括网络通信。文件描述符是一个非负整数,它是内核为了高效管理已打开的文件或其他 I/O 资源(如 socket、管道、设备等)而提供的一个抽象句柄。
当你创建一个 socket 时,系统会返回一个文件描述符,这个文件描述符就被称为 sockfd。之后,你就可以使用这个文件描述符来进行各种 socket 操作,比如绑定地址(bind)、监听连接(listen)、接受连接(accept)、读取数据(read)、发送数据(write 或 send)、关闭 socket(close)等。
例如,在下面的代码中,socket 函数创建了一个新的 socket,并返回了一个文件描述符,这个描述符被存储在变量 sockfd 中:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
在这里,AF_INET 指定了地址族为 IPv4,SOCK_STREAM 指定了 socket 类型为 TCP 流套接字。如果 socket 调用成功,sockfd 将是一个有效的文件描述符,用于后续的 socket 操作。如果调用失败,sockfd 将被设置为一个特定的错误值(通常是 -1),并且可以通过查看 errno 变量来获取失败的原因。
我们可以用一个类比来通俗易懂地解释 sockfd。
想象一下,你的电脑是一个大型的邮局,而你想通过邮局发送和接收信件。在邮局中,每个信箱都有一个唯一的编号,这个编号就像是文件描述符。当你创建一个 socket 时,就像是你在邮局申请了一个新的信箱,邮局会给你一个编号(即 sockfd),这样你就可以通过这个编号来发送和接收信件了。
当你想发送一封信时,你会把信放入对应编号的信箱中,然后邮局会帮你把信发送出去。同样地,当你创建了一个 socket 并获得了 sockfd,你就可以使用这个 sockfd 来进行网络通信,比如发送数据(send)和接收数据(recv)。
当你不再需要这个信箱时,你可以通知邮局回收这个信箱,这样其他人就可以使用这个编号了。在 socket 编程中,当你完成所有的通信任务后,你需要关闭 socket,释放 sockfd,这样系统就可以重新使用这个编号了。
所以,sockfd 就像是邮局信箱的编号,它帮助你管理和操作网络通信。
评论前必须登录!
注册