// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // 提供 close、read 和 write (这里通过 send 间接使用,因为 send 是 write 的一个更高级别的封装)
#include <arpa/inet.h> // 提供了用于网络地址转换的函数声明,如将点分十进制格式的 IP 地址转换为网络字节序,以及将端口号从主机字节序转换为网络字节序。
#define PORT 12000 // socket 绑定的端口好
#define BUFFER_SIZE 1024 // 缓存大小
int main() {
int server_fd, new_socket;
struct sockaddr_in address; // 结构体,用于存储 IPv4 地址和端口号的信息。
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) {
/*
AF_INET:指定地址族为 IPv4。
SOCK_STREAM:指定套接字类型为 TCP(面向连接的字节流)。
0:指定协议为 0,通常对于 SOCK_STREAM 和 AF_INET,这个参数为 0,意味着使用 TCP。
*/
perror("socket failed");
exit(EXIT_FAILURE);
}
// 绑定socket到端口
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET; // IPv4
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);
}
// 接受连接
while (1) {
printf("等待连接...\n");
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 接收数据
read(new_socket, buffer, BUFFER_SIZE);
printf("收到消息: %s\n", buffer);
// 发送数据
send(new_socket, hello, strlen(hello), 0);
printf("欢迎消息已发送\n");
// 关闭当前连接
close(new_socket);
}
// 关闭服务器socket
close(server_fd);
return 0;
}
编译:gcc -o server server.c
setsockopt()
#include <sys/socket.h>
int setsockopt(int socket, int level, int option_name,
const void *option_value, socklen_t option_len);
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
setsockopt() 函数,用于设置套接字的选项。是在创建一个服务器套接字时,配置套接字的一些行为或属性。SO_REUSEADDR
选项的设置允许在套接字关闭后,立即重用相同的地址(IP 和端口),而不必等到操作系统回收该端口。
level
:指定选项所在的协议层,通常是 SOL_SOCKET
,表示设置的是套接字级别的选项。
optval
:指向一个存储选项值的内存区域。这个值会根据不同的选项而变化,通常是一个整数或布尔值。
optlen
:optval
指向的内存区域的大小(字节数)。
int opt = 1;
opt 是 SO_REUSEADDR
选项的值通常是 1
(启用)或 0
(禁用)。sizeof(opt)
用来确定 opt
变量的大小。
setsockopt() 返回值:设置成功返回 0,否则返回 -1 。
address.sin_addr.s_addr = INADDR_ANY;
INADDR_ANY 是一个特殊的常量,值是 0.0.0.0,它代表任何可用的网络接口地址(所有的 IPv4 地址),通常用于绑定套接字到所有本地可用的网络接口,可以使得服务器程序更灵活地接受来自不同网络接口的请求。
bind()
#include <sys/socket.h>
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
返回值:绑定成功成功返回 0,否则返回 -1 。
listen()
#include <sys/socket.h>
int listen(int socket, int backlog);
返回值:监听成功返回 0,否则返回 -1 。
accept()
#include <sys/socket.h>
int accept(int socket, struct sockaddr *restrict address,
socklen_t *restrict address_len);
返回值:接受失败返回 -1,成功则返回接受套接字的非负文件描述。
read()
从套接字接收缓存中读取收到的数据。
#include <unistd.h>
ssize_t read(int fildes, void *buf, size_t nbyte);
成功完成后,将返回一个非负整数,表示实际读取的字节数。否则,函数将返回 -1 并设置 errno 以指示错误。
server 端 C++
// server.cpp
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 12000 // 端口
#define BUFFER_SIZE 1024
class Server {
public:
Server() {
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
std::cerr << "Socket creation failed" << std::endl;
exit(EXIT_FAILURE);
}
sockaddr_in address;
int addrlen = sizeof(address);
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) {
std::cerr << "Bind failed" << std::endl;
close(server_fd);
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0) {
std::cerr << "Listen failed" << std::endl;
close(server_fd);
exit(EXIT_FAILURE);
}
std::cout << "Server is listening on port " << PORT << std::endl;
}
~Server() {
close(server_fd);
}
void acceptConnection() {
sockaddr_in address;
int addrlen = sizeof(address);
int new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
if (new_socket < 0) {
std::cerr << "Accept failed" << std::endl;
return;
}
char buffer[BUFFER_SIZE];
int valread = read(new_socket, buffer, BUFFER_SIZE);
std::cout << "Message from client: " << buffer << std::endl;
const char *response = "Hello from server";
send(new_socket, response, strlen(response), 0);
std::cout << "Response sent to client" << std::endl;
close(new_socket);
}
private:
int server_fd;
};
int main() {
Server server;
while (true) {
server.acceptConnection();
}
return 0;
}
编译:g++ -o server server.cpp