基于Linux系统内核的select I/O复用cpp实现
实验结果就不贴了,即多个客户端可以和服务端进行通讯。实现了IO复用。
// // main.cpp // select_server // // Created by wenfan on 2021/2/15. // #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #define PORT 8888 #define FD_SIZE 1024 #define MESSAGE_SIZE 1024 const char *getIp(sockaddr_in *remote) { return inet_ntoa(remote->sin_addr); } int getPort(sockaddr_in *remote) { return ntohs(remote->sin_port); } int main(int argc, char*argv[]){ int ret = -1; int pid; int accept_fd = -1; int socket_fd = -1; int accept_fds[FD_SIZE] = {-1, }; int curpos = -1; int maxpos = 0; int backlog = 10; int flags = 1; //open REUSEADDR option int max_fd = -1; fd_set fd_sets; int events=0; struct sockaddr_in local_addr, remote_addr; //create a tcp socket socket_fd = socket(AF_INET, SOCK_STREAM, 0); if ( socket_fd == -1 ){ perror("create socket error"); exit(1); } //set option of socket ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags)); if ( ret == -1 ){ perror("setsockopt error"); } //NONBLOCK flags = fcntl(socket_fd, F_GETFL, 0); fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK); //set local address local_addr.sin_family = AF_INET; local_addr.sin_port = htons(PORT); local_addr.sin_addr.s_addr = INADDR_ANY; bzero(&(local_addr.sin_zero), 8); //bind socket ret = bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_in)); if(ret == -1 ) { perror("bind error"); exit(1); } ret = listen(socket_fd, backlog); if ( ret == -1 ){ perror("listen error"); exit(1); } max_fd = socket_fd; //每次都重新设置 max_fd for(int i=0; i< FD_SIZE; i++){ accept_fds[i] = -1; } for(;;) { FD_ZERO(&fd_sets); //清空sets FD_SET(socket_fd, &fd_sets); //将socket_fd 添加到sets for(int k=0; k < maxpos; k++){ if(accept_fds[k] != -1){ if(accept_fds[k] > max_fd){ max_fd = accept_fds[k]; } FD_SET(accept_fds[k], &fd_sets); //继续向sets添加fd } } //遍历所有的fd events = select( max_fd + 1, &fd_sets, NULL, NULL, NULL ); if(events < 0) { perror("select"); break; }else if(events == 0){ printf("select time out ......"); continue; }else if( events ){ printf("events:%d\n", events); if( FD_ISSET(socket_fd, &fd_sets)){ // 如果来的是新连接 int a = 0; for( ; a < FD_SIZE; a++){ if(accept_fds[a] == -1){ curpos = a; break; } } if(a == FD_SIZE){ printf("the connection is full!\n"); continue; } socklen_t addr_len = sizeof( struct sockaddr_in ); accept_fd = accept(socket_fd, (struct sockaddr *)&remote_addr, &addr_len); //创建一个新连接的fd int flags = fcntl(accept_fd, F_GETFL, 0); //取出新连接的 fd 的相关选项 fcntl(accept_fd, F_SETFL, flags|O_NONBLOCK); //设置为非阻塞 accept_fds[curpos] = accept_fd; //插入首次为-1的槽中 if(curpos+1 > maxpos){ maxpos = curpos + 1; } if(accept_fd > max_fd){ max_fd = accept_fd; } printf("new connection fd:%d, curpos = %d \n",accept_fd, curpos); } //从所有的连接槽中取出数据 for(int j=0; j < maxpos; j++ ){ if( (accept_fds[j] != -1) && FD_ISSET(accept_fds[j], &fd_sets)){ //有事件时 char in_buf[MESSAGE_SIZE]; memset(in_buf, 0, MESSAGE_SIZE); ret = recv(accept_fds[j], &in_buf, MESSAGE_SIZE, 0); if(ret == 0){ close(accept_fds[j]); accept_fds[j] = -1; } printf( "receive message:%s\n", in_buf ); send(accept_fds[j], (void*)in_buf, MESSAGE_SIZE, 0); } } } } printf("quit server...\n"); close(socket_fd); return 0; }
一大早更新一下,显示ip及端口
//
// main.cpp
// select_server
//
// Created by wenfan on 2021/2/15.
//
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unordered_map>
#define PORT 8888
#define FD_SIZE 1024
#define MESSAGE_SIZE 1024
char *getIp(sockaddr_in *remote) {
return inet_ntoa(remote->sin_addr);
}
int getPort(sockaddr_in *remote) {
return ntohs(remote->sin_port);
}
std::unordered_map<int,sockaddr_in*> fd_sock;
int main(int argc, char*argv[]){
int ret = -1;
int accept_fd = -1;
int socket_fd = -1;
int accept_fds[FD_SIZE] = {-1, };
int curpos = -1;
int maxpos = 0;
int backlog = 10;
int flags = 1; //open REUSEADDR option
int max_fd = -1;
fd_set fd_sets;
int events=0;
struct sockaddr_in local_addr, remote_addr;
//create a tcp socket
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if ( socket_fd == -1 ){
perror("create socket error");
exit(1);
}
//set option of socket
ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
if ( ret == -1 ){
perror("setsockopt error");
}
//NONBLOCK
flags = fcntl(socket_fd, F_GETFL, 0);
fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);
//set local address
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(PORT);
local_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(local_addr.sin_zero), 8);
//bind socket
ret = bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_in));
if(ret == -1 ) {
perror("bind error");
exit(1);
}
ret = listen(socket_fd, backlog);
if ( ret == -1 ){
perror("listen error");
exit(1);
}
max_fd = socket_fd; //每次都重新设置 max_fd
for(int i=0; i< FD_SIZE; i++){
accept_fds[i] = -1;
}
printf("server have started!\n");
for(;;) {
FD_ZERO(&fd_sets); //清空sets
FD_SET(socket_fd, &fd_sets); //将socket_fd 添加到sets
for(int k=0; k < maxpos; k++){
if(accept_fds[k] != -1){
if(accept_fds[k] > max_fd){
max_fd = accept_fds[k];
}
FD_SET(accept_fds[k], &fd_sets); //继续向sets添加fd
}
}
//遍历所有的fd
events = select( max_fd + 1, &fd_sets, NULL, NULL, NULL );
if(events < 0) {
perror("select");
break;
}else if(events == 0){
printf("select time out ......");
continue;
}else if( events ){
printf("events:%d\n", events);
if( FD_ISSET(socket_fd, &fd_sets)){ // 如果来的是新连接
int a = 0;
for( ; a < FD_SIZE; a++){
if(accept_fds[a] == -1){
curpos = a;
break;
}
}
if(a == FD_SIZE){
printf("the connection is full!\n");
continue;
}
socklen_t addr_len = sizeof( struct sockaddr_in );
//创建一个新连接的fd
accept_fd = accept(socket_fd, (struct sockaddr *)&remote_addr, &addr_len);
//保存fd和客户端地址的映射
fd_sock.insert(std::pair<int, sockaddr_in*>(accept_fd, &remote_addr));
int flags = fcntl(accept_fd, F_GETFL, 0); //取出新连接的 fd 的相关选项
fcntl(accept_fd, F_SETFL, flags|O_NONBLOCK); //设置为非阻塞
accept_fds[curpos] = accept_fd; //插入首次为-1的槽中
if(curpos+1 > maxpos){
maxpos = curpos + 1;
}
if(accept_fd > max_fd){
max_fd = accept_fd;
}
}
//从所有的连接槽中取出数据
for(int j=0; j < maxpos; j++ ){
if( (accept_fds[j] != -1) && FD_ISSET(accept_fds[j], &fd_sets)){ //有事件时
char in_buf[MESSAGE_SIZE];
memset(in_buf, 0, MESSAGE_SIZE);
ret = recv(accept_fds[j], &in_buf, MESSAGE_SIZE, 0);
if(ret == 0){
close(accept_fds[j]);
accept_fds[j] = -1;
}
auto iter = fd_sock.find(accept_fds[j]);
sockaddr_in* client = iter->second;
printf( "%s : %d send message: %s\n",getIp(client),getPort(client),in_buf);
send(accept_fds[j], (void*)in_buf, MESSAGE_SIZE, 0);
}
}
}
}
printf("quit server...\n");
close(socket_fd);
return 0;
}