避免端口冲突
- 使用系统分配端口:
- 当调用
bind
函数时,将端口号设为 0
,系统会自动分配一个可用端口。
- 示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 0
#define IP "127.0.0.1"
int main() {
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(IP);
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("Bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
socklen_t len = sizeof(servaddr);
if (getsockname(sockfd, (struct sockaddr *)&servaddr, &len) < 0) {
perror("getsockname failed");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("Allocated port: %d\n", ntohs(servaddr.sin_port));
close(sockfd);
return 0;
}
- 手动选择知名端口范围外的端口:
- 知名端口范围是
0 - 1023
。选择 1024
及以上的端口可以减少与系统服务的冲突。
- 例如,选择
50000
端口:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 50000
#define IP "127.0.0.1"
int main() {
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(IP);
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("Bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
close(sockfd);
return 0;
}
提高端口复用效率以应对高并发
- 设置
SO_REUSEADDR
选项:
- 该选项允许在
TIME_WAIT
状态下的端口被复用。
- 示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 50000
#define IP "127.0.0.1"
int main() {
int sockfd;
int opt = 1;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
close(sockfd);
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(IP);
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("Bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
close(sockfd);
return 0;
}
- 设置
SO_REUSEPORT
选项(Linux 3.9 及以上):
- 该选项允许多个套接字绑定到同一个地址和端口,提高高并发性能。
- 示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 50000
#define IP "127.0.0.1"
int main() {
int sockfd;
int opt = 1;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const void *)&opt, sizeof(opt)) < 0) {
perror("setsockopt(SO_REUSEPORT) failed");
close(sockfd);
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(IP);
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("Bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
close(sockfd);
return 0;
}
在不同安全策略下保障端口使用的安全性
- 安全子网(高安全级别):
- 使用加密通信:例如使用
OpenSSL
库实现 SSL/TLS
加密。
- 限制连接源:通过
iptables
等防火墙工具,只允许特定子网的连接。
- 示例代码(简单的
SSL/TLS
服务器示例,需安装 openssl
库):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define PORT 50000
#define IP "127.0.0.1"
void handleErrors() {
ERR_print_errors_fp(stderr);
abort();
}
int main() {
int sockfd;
struct sockaddr_in servaddr;
SSL_CTX *ctx;
SSL *ssl;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(IP);
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("Bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
if (listen(sockfd, 5) < 0) {
perror("Listen failed");
close(sockfd);
exit(EXIT_FAILURE);
}
SSL_library_init();
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new(TLS_server_method());
if (!ctx) {
handleErrors();
}
if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) <= 0) {
handleErrors();
}
if (SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) {
handleErrors();
}
int clientfd = accept(sockfd, NULL, NULL);
if (clientfd < 0) {
perror("Accept failed");
close(sockfd);
SSL_CTX_free(ctx);
exit(EXIT_FAILURE);
}
ssl = SSL_new(ctx);
SSL_set_fd(ssl, clientfd);
if (SSL_accept(ssl) <= 0) {
ERR_print_errors_fp(stderr);
} else {
char buf[1024];
int len = SSL_read(ssl, buf, sizeof(buf) - 1);
if (len > 0) {
buf[len] = '\0';
printf("Received: %s\n", buf);
}
}
SSL_free(ssl);
close(clientfd);
close(sockfd);
SSL_CTX_free(ctx);
EVP_cleanup();
return 0;
}
- 非安全子网(低安全级别):
- 进行身份验证:例如使用用户名和密码验证连接。
- 监控端口活动:通过
netstat
等工具或自定义程序监控端口连接情况。
- 示例代码(简单的用户名密码验证示例):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 50000
#define IP "127.0.0.1"
#define USERNAME "admin"
#define PASSWORD "password"
int main() {
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(IP);
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("Bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
if (listen(sockfd, 5) < 0) {
perror("Listen failed");
close(sockfd);
exit(EXIT_FAILURE);
}
int clientfd = accept(sockfd, NULL, NULL);
if (clientfd < 0) {
perror("Accept failed");
close(sockfd);
exit(EXIT_FAILURE);
}
char buf[1024];
int len = recv(clientfd, buf, sizeof(buf) - 1, 0);
if (len > 0) {
buf[len] = '\0';
char *token = strtok(buf, ":");
char *user = token;
char *pass = strtok(NULL, "\n");
if (strcmp(user, USERNAME) == 0 && strcmp(pass, PASSWORD) == 0) {
send(clientfd, "Authenticated\n", strlen("Authenticated\n"), 0);
} else {
send(clientfd, "Authentication failed\n", strlen("Authentication failed\n"), 0);
}
}
close(clientfd);
close(sockfd);
return 0;
}