网络编程实战学习笔记(十三)-select多路复用
Day20
基础知识
1.select 方法是多个 UNIX 平台支持的非常常见的 I/O 多路复用技术,它通过描述符集合来表示检测的 I/O 对象,通过三个不同的描述符集合来描述 I/O 事件 :可读、可写和异常。但是 select 有一个缺点,那就是所支持的文件描述符的个数是有限的。在 Linux 系统中,select 的默认最大值为 1024。
demo
client
//
// Created by root on 11/9/19.
//
//#include"lib/common.h"
#include<iostream>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <unistd.h>
#include "message_objecte.h"
#include <signal.h>
#define SERV_PORT 43211
#define MAXLINE 4096
#define UNIXSTR_PATH "/var/lib/unixstream1.sock"
#define LISTENQ 1024
#define BUFFER_SIZE 4096
#define MAXLINE 4096
int tcp_client(const std::string &ip,int16_t port){
//1.创建
int socket_fd=socket(AF_INET,SOCK_STREAM,0);
//2.初始化
struct sockaddr_in clien_addr;
bzero(&clien_addr,sizeof(clien_addr));
clien_addr.sin_port=htons(port);
clien_addr.sin_family=AF_INET;
inet_pton(AF_INET,ip.c_str(), &clien_addr.sin_addr);
socklen_t len= sizeof(clien_addr);
//3.连接
int rc=connect(socket_fd,(struct sockaddr*)&clien_addr,len);
if(rc<0){
error(1,errno,"connect error");
return -1;
}
return socket_fd;
}
int main()
{
int socket_fd=tcp_client("127.0.0.1",SERV_PORT);
char recv_line[MAXLINE]={0},send_line[MAXLINE]={0};
int n;
fd_set readmask;
fd_set allreads;
FD_ZERO(&allreads);//初始化
FD_SET(0,&allreads);//设置标准IO感知
FD_SET(socket_fd,&allreads);//设置套接字感知
for(;;){
//每次检测重新设置套接字描述符
readmask=allreads;
int rc=select(socket_fd+1,&readmask,NULL,
NULL,NULL);
if(rc<=0){
error(1,errno,"select failed");
}
if(FD_ISSET(socket_fd,&readmask)){
n=read(socket_fd,recv_line,sizeof(recv_line));
if(n<0){
error(1,errno,"read failed");
}else if(n==0){
error(1,0,"server closed");
}
recv_line[n]=0;
fputs(recv_line,stdout);
fputs("\n",stdout);
}
if(FD_ISSET(STDIN_FILENO,&readmask)){
if(fgets(send_line,MAXLINE,stdin)!=NULL){
int i=strlen(send_line);
if(send_line[i-1]=='\n'){
send_line[i-1]=0;
}
printf("now sending %s\n",send_line);
//不能用size_t 因为size_t是unsigned int不会小于0
ssize_t rt=write(socket_fd,send_line,strlen(send_line));
if(rt<0){
error(1,errno,"write failed");
}
printf("send bytes:%zu\n",rt);
}
}
}
}
server
//
// Created by root on 11/9/19.
//
//#include"lib/common.h"
//homework_server 只支持一次服务一个客户端,这里用select多路复用的方式实现下server
#include<iostream>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <unistd.h>
#include "message_objecte.h"
#include <signal.h>
#define SERV_PORT 43211
#define MAXLINE 4096
#define UNIXSTR_PATH "/var/lib/unixstream1.sock"
#define LISTENQ 1024
#define BUFFER_SIZE 4096
#define MAXLINE 4096
static int count;
const int fds_nums=64;
static void sig_int(int signo){
printf("\nreceived %d datagrams\n",count);
exit(0);
}
int main()
{
int fds_array[64];//fds_array[64]={-1} 只把第一元素初始话为了-1;
memset(fds_array,-1, sizeof(fds_array));
//1.create
int listen_fd=socket(AF_INET,SOCK_STREAM,0);
//2.init
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_port=htons(SERV_PORT);
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=INADDR_ANY;
//3.set reuse(SOL_SOCKET 套接字级别)
int on=1;
setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//4.bind
socklen_t len= sizeof(server_addr);
int rt1=bind(listen_fd,(struct sockaddr*)&server_addr,len);
if(rt1<0){
error(1,errno,"bind failed");
}
//5.listen
int rt2=listen(listen_fd,1024);
if(rt2<0){
error(1,errno,"listen failed");
}
fd_set allreads;
fd_set readmasks;
FD_ZERO(&allreads);
FD_SET(0,&allreads);
FD_SET(listen_fd,&allreads);
int max_fd=-1;//最大文件描述符的下标
int i=0;
fds_array[i]=listen_fd;
struct timeval timeout={5,0};//5s 0毫秒
int done=0;
while(!done)
{
max_fd = -1;
int i=0;
for(;i<fds_nums;++i){
if(fds_array[i]>0){
//设置fds_array[i]的位
FD_SET(fds_array[i],&allreads);
//计算最大fd
max_fd=max_fd>fds_array[i]?max_fd:fds_array[i];
}
}
readmasks=allreads;
//最后timeout为空表示阻塞等待
int ret=select(max_fd+1,&readmasks,NULL,NULL,NULL);
switch(ret){
case 0:
//超时而且没有任何描述符就绪
printf("timeout...\n");
break;
case -1:
perror("select error");
exit(1);
default:
{
i=0;
for(;i<fds_nums;++i){
if(fds_array[i]==listen_fd &&
FD_ISSET(listen_fd,&readmasks)){//有连接进来
//是监听套接字,且就绪
struct sockaddr_in client_addr;
socklen_t len= sizeof(client_addr);
int connfd=accept(listen_fd,(struct sockaddr*)&client_addr,&len);
if(connfd<0){
error(1,errno,"connect error");
}
printf("get a new client: %s:%d\n",inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
//将客户端加描述符集合
int j=0;
for(;j<fds_nums;++j){
if(fds_array[j]==-1){
fds_array[j]=connfd;
break;
}
}
if(j==fds_nums){
//空间已用完
close(connfd);
}
}
else{//有事件感知
if(fds_array[i]>0&&
FD_ISSET(fds_array[i],&readmasks)){
char readbuf[1024]={0};
char writebuf[1024]={0};
//从fd_array[i]这个客户端读取数据
ssize_t rt=read(fds_array[i],readbuf,sizeof(readbuf)-1);
if(rt<0){
error(1,errno,"read error");
}else if(rt==0){
printf("client close");
close(fds_array[i]);
fds_array[i]=-1;
}else{
readbuf[rt]=0;
printf("recvice:%s\n",readbuf);
sprintf(writebuf,"Hi,%s\n",readbuf);
printf("send %s\n",writebuf);
fflush(stdout);//刷新输出缓冲,否则输出信息不会打出来
send(fds_array[i],writebuf,strlen(writebuf),0);
}
}
}
}
}
break;
}
}
}