C# Socket编程模型
- 作者: 五速梦信息网
- 时间: 2026年04月20日 04:36
#define SEVER_PORT 6666 #define BUFFER_SIZE 1024 #define MAX_EVENTS 10 #define handle_error(cmd,result)
if(result<0){ \
perror(cmd); \
exit(EXIT_FAILURE); \
} \
char *read_buf=NULL; char *write_buf=NULL; void init_buf() {
read_buf=malloc(sizeof(char)* BUFFER_SIZE);
//读内存分配判断
if(!read_buf)
{
printf("读缓存创建异常,断开连接\n");
exit(EXIT_FAILURE);
}
//写内存分配判断
write_buf=malloc(sizeof(char)* BUFFER_SIZE);
if(!write_buf)
{
printf("写缓存创建异常,断开连接\n");
exit(EXIT_FAILURE);
}
memset(read_buf,0,BUFFER_SIZE);
memset(write_buf,0,BUFFER_SIZE);
} void clear_buf(char *buf) {
memset(buf,0,BUFFER_SIZE);
} void set_nonblocking(int sockfd) {
int opts=fcntl(sockfd,F_GETFL);
if(opts<0)
{
perror("fcntl(F_GETFL)");
exit(EXIT_FAILURE);
}
opts|=O_NONBLOCK;
int res=fcntl(sockfd,F_SETFL,opts);
if(res<0)
{
perror("fcntl(F_GETFL)");
exit(EXIT_FAILURE);
}
} int main(int argc, char const *argv[]) {
//初始化读写缓冲区
init_buf();
//声明sockfd,clientfd
int sockfd,client_fd,temp_result;
//声明服务端与客户端地址
struct sockaddr_in server_addr,client_addr;
memset(&server_addr,0,sizeof(server_addr));
memset(&client_addr,0,sizeof(client_addr));
//声明IP协议
server_addr.sin_family=AF_INET;
//绑定主机地址
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//绑定端口
server_addr.sin_port=htons(SEVER_PORT);
//创建socket
sockfd=socket(AF_INET,SOCK_STREAM,0);
handle_error("socket",sockfd);
//绑定地址
temp_result=bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
handle_error("bind",temp_result);
//进入监听
temp_result=listen(sockfd,128);
handle_error("listen",temp_result);
//将sockfd设为非阻塞模式
set_nonblocking(sockfd);
int epollfd,nfds;
struct epoll_event ev,events[MAX_EVENTS];
//创建epoll
epollfd=epoll_create1(0);
handle_error("epoll_create1",epollfd);
//将sockfd加入到监控列表
ev.data.fd=sockfd;
//将关联的文件描述符设为可读,可读说明有连接进入,就会被epoll触发
ev.events=EPOLLIN;
temp_result=epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&ev);
handle_error("epoll_ctl",temp_result);
socklen_t client_addr_len=sizeof(client_addr);
//接受client连接
while (1)
{
//挂起等待,有可读信息
//nfds表示有多少个客户端连接与多少条消息
nfds=epoll_wait(epollfd,events,MAX_EVENTS,-1);
handle_error("epoll_wait",nfds);
for (int i = 0; i < nfds; i++)
{
//第一个是sockfd,要预处理一下。
if(events[i].data.fd==sockfd)
{
client_fd=accept(sockfd,(struct sockaddr *)&client_addr,&client_addr_len);
handle_error("accept",client_fd);
set_nonblocking(client_fd);
printf(“与客户端from %s at PORT %d 文件描述符 %d 建立连接\n”,inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd); //将获取到的client连接也添加到监控列表
ev.data.fd=client_fd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,client_fd,&ev);
}
//既有新的客户端连接,又有旧客户端发送消息
else if(events[i].events&EPOLLIN)
{
//老连接有数据
int count=0,send_count=0;
client_fd=events[i].data.fd;
while ((count=recv(client_fd,read_buf,BUFFER_SIZE,0)>0))
{
printf("receive message from client_fd: %d: %s \n",client_fd,read_buf);
clear_buf(read_buf);
strcpy(write_buf,“receive~\n”);
send_count=send(client_fd,write_buf,strlen(write_buf),0);
handle_error("send",send_count);
clear_buf(write_buf);
}
if(count==-1&&errno==EAGAIN)
{
printf("当前批次已经读取完毕。\n");
}
else if(count==0)
{
printf("客户端client_fd:%d请求关闭连接......\n",client_fd);
strcpy(write_buf,"recevie your shutdown signal 收到你的关闭信号\n");
send_count=send(client_fd,write_buf,strlen(write_buf),0);
handle_error("send",send_count);
clear_buf(write_buf);
//从epoll文件描述法符中移除该client_fd
epoll_ctl(epollfd,EPOLL_CTL_DEL,client_fd,NULL);
printf(“释放client_fd:%d资源\n”,client_fd);
shutdown(client_fd,SHUT_WR);
close(client_fd);
}
}
}
} printf(“服务端关闭后资源释放\n”);
close(epollfd);
close(sockfd);
free(read_buf);
free(write_buf);
return 0; } “`
理论与现实的割裂
从上面的理论可以看出,AIO似乎是版本答案,在C#中,AIO已经充斥着每一个角落,但在JAVA的世界中,更加主流的是NIO,这是为什么呢?
1. Linux的支持不足
Linux 内核直到 3.11 版本(2013 年)才支持真正的异步 IO(io_uring),从而间接影响了JAVA的发展,Java的 AIO直到 2011 年Java 7才正式发布,而其前一代 NIO已发展近 10 年。
而Windows的IOCP在Windows NT 4.0 (1996年)就登上了历史舞台,加上C#起步较晚,没有历史包袱,所以对AIO支持力度更大,尤其是2012年发布了async/await异步模型后,解决了回调地狱,实现了1+1>3的效果。
2. JAVA的路径依赖
NIO生态过于强大,尤其是以Netty/Redis为首的经典实现,实在是太香了!
3. 理论优势并未转换为实际收益
AIO的性能在特定场景(如超大规模文件读写、长连接低活跃)下可能优于NIO,但在互联网场景中,NIO的足够高效,比如HTTP请求,AIO的异步回调优势相对轮询并不明显。
| 维度 | Java AIO未普及的原因 | C# AIO普及的原因 |
|---|---|---|
| 历史发展 | NIO早于AIO 9年推出,生态成熟;AIO定位模糊,未解决NIO的核心痛点(如编程复杂度) | AIO与async/await同步推出,解决了异步编程的“回调地狱”,成为高并发编程的默认选择 |
| 跨平台 | 需适配多系统异步机制(如Linux的epoll、macOS的kqueue),实际性能提升有限 |
早期绑定Windows IOCP,性能稳定;跨平台后对AIO需求不迫切 |
| 生态 | Netty等NIO框架统治市场,切换AIO成本高 | 缺乏NIO统治级框架,AIO通过async/await成为原生选择 |
| 开发者习惯 | NIO代码虽复杂,但通过框架封装已足够易用;AIO回调模式学习成本更高 | async/await语法糖让异步代码接近同步,开发者更易接受 |
| 性能场景 | 大多数场景下NIO已足够高效,AIO的优势未显著体现 | Windows IOCP场景下AIO性能优势明显,且覆盖主流企业级需求 |
> 说人话就是,Netty太香了,完全没动力切换成AIO,顺带吐槽C#中没有类似的框架。dotnetty不算,已经停止更新了。
- 上一篇: C# Office COM 加载项
- 下一篇: C# 多项目打包时如何将项目引用转为包依赖
相关文章
-
C# Office COM 加载项
C# Office COM 加载项
- 互联网
- 2026年04月20日
-
C# LINQ 快速入门实战指南,建议收藏学习!
C# LINQ 快速入门实战指南,建议收藏学习!
- 互联网
- 2026年04月20日
-
C# 窗口过程消息处理 WndProc
C# 窗口过程消息处理 WndProc
- 互联网
- 2026年04月20日
-
C# 多项目打包时如何将项目引用转为包依赖
C# 多项目打包时如何将项目引用转为包依赖
- 互联网
- 2026年04月20日
-
C#多线程编程精要:从用户线程到线程池的效能进化论
C#多线程编程精要:从用户线程到线程池的效能进化论
- 互联网
- 2026年04月20日
-
C#基础:枚举、数组、类型、函数等解析
C#基础:枚举、数组、类型、函数等解析
- 互联网
- 2026年04月20日





