java socket 多路复用_socket中的多路复用

好多文档上都在说,select 比阻塞好啊。。。我倒觉着,不尽然。反正东西总是因为有用处才会出现,这里写下我对select 的看法。

select用在一个进程同时监听几个socket端口的情况。在这种情况下,程序可以阻塞在select,当出现可以操作的情况时,再对相应的端口进行读取或写入操作。因为在阻塞的情况下,程序不占用CPU,所以,说select比非阻塞好,而因为同时能够处理多个socket,所以说比阻塞方式IO好。

这里个人感觉,也有可能是自个的应用没有这个需求,如果我需要多个socket进行操作,我完全可以fork进程出来,让每个进程操作一个端口并阻塞在该端口的通讯上。由于各个端口互不影响,多进程自个阻塞,也不占用CPU,编程还相对多路复用方便,不是更实用吗?? 猜想,是不是多路复用适合于连接数太多,没有办法每个连接都进行fork的情况? 先想到这里,后面附上自个调试的代码,分两个程序,一个host,一个client,两个程序建立5个连接,测试多路复用同时监听5个连接的能力。

host.c:

#include #include #include #include #include #include #include #include #include #include #include #include #define NUM 5

void main(int argc, char **argv[])

{

int fd, fd1;

int len, i;

struct sockaddr_in add;

struct fd_set fds;

struct timeval timeout;

char msg[100];

int Cfd[NUM];

int count = 0;

int code;

timeout.tv_sec = 2;

timeout.tv_usec = 0;

len = sizeof(add);

bzero(&add, len);

add.sin_family=AF_INET;

add.sin_port=htons(10000);

add.sin_addr.s_addr=htonl(INADDR_ANY);

fd = socket(AF_INET, SOCK_STREAM, 0);

if(fd < 0) printf("socket err\n");

bind(fd, (struct sockaddr *)&add, len);

listen(fd, SOCK_STREAM);

while(1){

fd1 = accept(fd, (struct sockaddr *)&add, &len);

if(fd1>0) {

Cfd[count++]=fd1;

printf("accept %d \n", count);

if(count == NUM) break;

}

sleep(1);

}

printf("end accept\n");

while(1){

printf("now circlie\n");

FD_ZERO(&fds);

fd1 = 0;

for(i=0;iFD_SET(Cfd[i],&fds);

if(fd1< Cfd[i])fd1 = Cfd[i];

}

fd1 = fd1+1;

code = select(fd1, &fds, NULL, NULL, &timeout);

printf("select:%d\n", code);

if(code > 0){

for(i=0;iif(FD_ISSET(Cfd[i], &fds)){

memset(msg, 0,sizeof(msg));

recv(Cfd[i], msg, 100, 0);

printf("%d:recv %s\n",i,msg);

}

}

}

sleep(1);

}

close(fd);

for(i=0;i}

cli.c:

#include #include #include #include #include #include #include #include #include #include #define NUM 5

void main(int argc, char **argv[])

{

int fd[NUM];

int len, i;

struct sockaddr_in add;

char msg[100];

len = sizeof(add);

bzero(&add, len);

add.sin_family=AF_INET;

add.sin_port=htons(10000);

add.sin_addr.s_addr=inet_addr("10.144.15.234");

for(i=0;ifd[i] = socket(AF_INET, SOCK_STREAM, 0);

if(fd[i]<0) printf("fd%d socket err\n", i);

connect(fd[i], (struct sockaddr *)&add, len);

printf("connect: %d\n", i);

}

printf("connect end\n");

sleep(10);

//上面的延时用于验证select的主机延时阻塞功能

for(i=4;i>-1;i--){

sprintf(msg, "i am %d\n", i);

send(fd[i], msg, strlen(msg), 0);

printf("send :%s\n", msg);

sleep(5);

}

for(i=0;i}

makefile:

LIB = -lsocket -lnsl

all : clean  host  cli

.PHONY : all

clean :

-rm *.o sem core

cli : cli.o

cc -o cli cli.o $(LIB)

cli.o : cli.c

cc -c cli.c $(LIB)

host : host.o

cc -o host host.o $(LIB)

host.o : host.c

cc -c host.c $(LIB)

这里注意一点:

如果希望程序在接受数据时延时阻塞一定按照下面的写法做:

select(fd1, &fds, NULL, NULL, &timeout);

因为select的第2~4个参数分表表示接受阻塞、发送阻塞、错误阻塞,这里只有提供第二个参数才能达到相应的效果。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
socket编程多路复用是指通过一种机制,使一个进程可以监视多个文件描述符,一旦某个文件描述符就绪(一般是读写操作准备就绪),能够通知程序进行相应的读写操作。在Linux,常用的多路复用机制有select、poll和epoll。其,select是最古老的多路复用机制,poll是select的改进版,而epoll是最新、最高效的多路复用机制。多路复用机制可以大大提高程序的并发性能,使得程序可以同时处理多个客户端请求。 下面是一个简单的使用select实现多路复用的流程图和代码示例: 流程图: ``` 1. 创建socket并绑定端口 2. 将socket设置为非阻塞模式 3. 创建fd_set集合,并将socket加入集合 4. 进入循环,调用select函数,等待文件描述符就绪 5. 如果socket就绪,表示有新的客户端连接请求,调用accept函数接受连接 6. 如果其他文件描述符就绪,表示有客户端发送数据,调用recv函数接收数据并处理 7. 回到步骤4,继续等待文件描述符就绪 ``` 代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/select.h> #define MAX_CLIENTS 10 #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { int server_fd, client_fd, max_fd, activity, i, valread, sd; struct sockaddr_in address; char buffer[BUFFER_SIZE] = {0}; fd_set readfds; // 创建socket并绑定端口 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(atoi(argv[1])); if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } if (listen(server_fd, MAX_CLIENTS) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } // 将socket设置为非阻塞模式 int flags = fcntl(server_fd, F_GETFL, 0); fcntl(server_fd, F_SETFL, flags | O_NONBLOCK); // 创建fd_set集合,并将socket加入集合 FD_ZERO(&readfds); FD_SET(server_fd, &readfds); max_fd = server_fd; // 进入循环,调用select函数,等待文件描述符就绪 while (1) { activity = select(max_fd + 1, &readfds, NULL, NULL, NULL); if (activity < 0) { perror("select error"); exit(EXIT_FAILURE); } // 如果socket就绪,表示有新的客户端连接请求,调用accept函数接受连接 if (FD_ISSET(server_fd, &readfds)) { if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept error"); exit(EXIT_FAILURE); } printf("New connection, socket fd is %d, ip is : %s, port : %d\n", client_fd, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); // 将新的客户端socket加入集合 FD_SET(client_fd, &readfds); if (client_fd > max_fd) { max_fd = client_fd; } } // 如果其他文件描述符就绪,表示有客户端发送数据,调用recv函数接收数据并处理 for (i = server_fd + 1; i <= max_fd; i++) { sd = i; if (FD_ISSET(sd, &readfds)) { if ((valread = recv(sd, buffer, BUFFER_SIZE, 0)) == 0) { // 客户端关闭连接 printf("Client disconnected, socket fd is %d\n", sd); close(sd); FD_CLR(sd, &readfds); } else { // 处理客户端发送的数据 printf("Received message from client, socket fd is %d, message is %s\n", sd, buffer); memset(buffer, 0, BUFFER_SIZE); } } } } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值