套接字选项设置
- 设置接收缓冲区大小:通过
setsockopt
函数增大接收缓冲区大小,提高接收数据的能力。
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#define BUFFER_SIZE 1024 * 1024 // 1MB
int main() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
return -1;
}
int recvbuf = BUFFER_SIZE;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf)) < 0) {
perror("setsockopt SO_RCVBUF failed");
close(sockfd);
return -1;
}
// 后续UDP相关操作...
close(sockfd);
return 0;
}
- 启用广播或多播:如果应用场景需要,可以设置相应选项。例如启用广播:
int broadcast = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) < 0) {
perror("setsockopt SO_BROADCAST failed");
close(sockfd);
return -1;
}
缓冲区管理
- 环形缓冲区:使用环形缓冲区(circular buffer)来管理接收和发送的数据。它可以高效地处理数据的进出,避免频繁的内存分配和释放。
#define CIRCULAR_BUFFER_SIZE 1024
typedef struct {
char buffer[CIRCULAR_BUFFER_SIZE];
int head;
int tail;
} CircularBuffer;
void initCircularBuffer(CircularBuffer *cb) {
cb->head = 0;
cb->tail = 0;
}
int writeToCircularBuffer(CircularBuffer *cb, const char *data, int len) {
int available = (cb->head - cb->tail + CIRCULAR_BUFFER_SIZE) % CIRCULAR_BUFFER_SIZE;
if (len > available) {
return -1; // 缓冲区空间不足
}
for (int i = 0; i < len; i++) {
cb->buffer[cb->head] = data[i];
cb->head = (cb->head + 1) % CIRCULAR_BUFFER_SIZE;
}
return len;
}
int readFromCircularBuffer(CircularBuffer *cb, char *data, int len) {
int available = (cb->head - cb->tail + CIRCULAR_BUFFER_SIZE) % CIRCULAR_BUFFER_SIZE;
if (len > available) {
len = available;
}
for (int i = 0; i < len; i++) {
data[i] = cb->buffer[cb->tail];
cb->tail = (cb->tail + 1) % CIRCULAR_BUFFER_SIZE;
}
return len;
}
- 零拷贝技术:在数据传输过程中,尽量减少数据的拷贝次数。例如使用
sendmsg
和recvmsg
函数结合iovec
结构,可以实现部分零拷贝功能。
多线程/多进程模型选择与实现
- 多线程模型:适用于I/O密集型任务,线程间共享内存方便数据通信。
#include <pthread.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#define THREAD_NUM 5
#define BUFFER_SIZE 1024
void* udpReceive(void* arg) {
int sockfd = *((int*)arg);
char buffer[BUFFER_SIZE];
struct sockaddr_in servaddr, cliaddr;
socklen_t len = sizeof(cliaddr);
while (1) {
int n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *) &cliaddr, &len);
buffer[n] = '\0';
printf("Received: %s\n", buffer);
}
pthread_exit(NULL);
}
int main() {
int sockfd = socket(AF_INET, SOCK_DUDP, 0);
if (sockfd < 0) {
perror("socket creation failed");
return -1;
}
pthread_t threads[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++) {
pthread_create(&threads[i], NULL, udpReceive, &sockfd);
}
for (int i = 0; i < THREAD_NUM; i++) {
pthread_join(threads[i], NULL);
}
close(sockfd);
return 0;
}
- 多进程模型:适用于CPU密集型任务,进程间相互独立,稳定性高。
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define PROCESS_NUM 5
#define BUFFER_SIZE 1024
void childProcess(int sockfd) {
char buffer[BUFFER_SIZE];
struct sockaddr_in servaddr, cliaddr;
socklen_t len = sizeof(cliaddr);
while (1) {
int n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *) &cliaddr, &len);
buffer[n] = '\0';
printf("Received: %s\n", buffer);
}
}
int main() {
int sockfd = socket(AF_INET, SOCK_DUDP, 0);
if (sockfd < 0) {
perror("socket creation failed");
return -1;
}
for (int i = 0; i < PROCESS_NUM; i++) {
pid_t pid = fork();
if (pid == 0) {
childProcess(sockfd);
_exit(0);
} else if (pid < 0) {
perror("fork failed");
close(sockfd);
return -1;
}
}
for (int i = 0; i < PROCESS_NUM; i++) {
wait(NULL);
}
close(sockfd);
return 0;
}
性能分析思路
- 吞吐量:通过记录单位时间内成功接收或发送的数据量来衡量。可以在接收或发送函数前后记录时间戳,计算数据量差值与时间差值的比值。
- 延迟:记录从数据发送到接收到响应的时间间隔。对于UDP无连接协议,可以在发送数据时附带时间戳,接收端收到数据后计算时间差。
- 资源利用率:使用
top
、ps
等工具监控CPU、内存等资源的使用情况。在代码中,可以通过getrusage
函数获取进程的资源使用信息,分析不同优化策略下资源的消耗情况。例如,多线程模型可能会在高并发下出现线程竞争导致CPU使用率升高,而多进程模型可能会消耗更多内存。通过调整缓冲区大小、线程/进程数量等参数,观察资源利用率和性能指标的变化,找到最优配置。