相较于前面的多线程服务器,多进程服务器一个accept监听所有客户端的连接,导致服务器的接收端异常繁忙,也就是什么事都需要服务器连接端来完成;对于多路io转接,则是服务器老板安排了一个小助手来帮忙,即对于所有请求,先由小助手进行分类,需要服务器端套接字老板的时候再联系老板,,任何老板再进行处理与客户端建立连接,或者进行通信;;
nfds:监听所有文件描述符中,最大的文件描述符加一;
readfds:读文件描述符监听集合
writefds:写文件描述符监听集合
exceptfds:异常文件描述符监听集合
timeout: 大于0设置监听超时时长,NULL,阻塞监听,0,非阻塞监听,轮询
void FD_CLR(int fd, fd_set *set);//将一个文件描述符从监听集合中移除;
int FD_ISSET(int fd, fd_set *set);//判断一个文件描述符是否在集合中;
void FD_SET(int fd, fd_set *set);//-----将待监听的文件符,添加到集合中;
void FD_ZERO(fd_set *set);//-----清空一个文件描述符集合
缺点:
监听数受文件描述符限制, 最大1024
检测满足条件的fd,自己添加业务逻辑提高小, 提高了编码难度;
优点:
跨平台。win, linux, macOS, Unix, mips, 类unix;
这里使用的是Ipv4,TCP套接字,所以使用的接口是:lfd = socket(AF_INET, SOCK_STREAM, 0)
如果是IPV6把AF_INET后面加个6,如果是UDP,那就是把第二个改成SOCK_DGRAM;
主要是端口复用:固定写法就好,第一个参数是socket函数返回值套接字的文件描述符:int opt = 1; setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(lfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
b这个函数主要目的就是将服务器的地址结构绑定到套接字lfd上,所以开始要设置服务器的ser_addr:ser_addr.sin_family = AF_INET, ser_addr.sin_port = htons(8888);ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
端口8888是可由自己设置的,,
设置监听的上限的函数,,并不是阻塞监听的函数listen(lfd, 128);
主要是初始化读文件描述符监听集合(主要使用的集合),然后设置辅助的读文件描述符监听集合 FD_ZERO(&aset);
FD_SET(lfd, &aset);
返回的值就是这个时候有多少客户端请求服务端读取它们的信息或是连接请求或是读写请求int n = tcp.Select(maxfd + 1, &readSet, NULL, NULL, NULL);
即是由服务器端的套接字发出读请求,即要读取客户端的连接请求
if (FD_ISSET(lfd, &readSet)){
//cfd = do_accept(lfd, tcp_select);
cfd = tcp.Accept(lfd, (struct sockaddr*)&cli_addr, &cli_addr_len);
FD_SET(cfd, &allSet);
if (cfd > maxfd)maxfd = cfd;
if (n == 1){
continue;
}
}
for (int i = lfd; i <= maxfd; i ++){
if (FD_ISSET(i, &readSet)){
//do_read(i);
ret = tcp.Read(i, buf, sizeof(buf), allSet);
if (ret > 0){
for (int j = 0; j < ret; j ++){
buf[j] = toupper(buf[j]);
}
tcp.Write(STDOUT_FILENO, buf, ret);
tcp.Write(i, buf, ret);
}
}
}
#ifndef __SELECTTCP_H_
#define __SELECTTCP_H_
#include<string>
#include<iostream>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
using namespace std;
class SelectTcp
{
public:
SelectTcp(string note);
int Socket(int domain, int type, int procotol);
void Setsockopt(int lfd, int level, int optname, const void* optval, socklen_t optlen);
void setAddr(struct sockaddr_in& serv_addr, int flg);
void Bind(int lfd, const struct sockaddr* serv_addr, socklen_t size);
void Listen(int lfd, int backlog);
int Accept(int lfd, struct sockaddr* cli_addr, socklen_t* cli_addr_len);
int Select(int nfds, fd_set* readfds, fd_set* writefds, fd_set*exceptfds, struct timeval* timeout);
int Read(int fd, void* buf, size_t size, fd_set& set);
int Write(int fd, const void* buf, size_t size);
};
SelectTcp::SelectTcp(string note){
cout << note << endl;
}
int SelectTcp::Socket(int domain, int type, int procotol){
int ret = socket(domain, type, procotol);
if (ret == -1){
cout << "socket() occurr fail " << strerror(errno) << endl;
exit(1);
}
return ret;
}
void SelectTcp::Setsockopt(int lfd, int level, int optname, const void* optval, socklen_t optlen){
int ret = setsockopt(lfd, level, optname, optval, optlen);
if (ret == -1){
cout << "setsockopt() occurr fail " << strerror(errno) << endl;
exit(1);
}
}
void SelectTcp::setAddr(struct sockaddr_in& serv_addr, int flg){
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8888);
if (flg == 1){ //服务端
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
} else { //客户端
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
}
}
void SelectTcp::Bind(int lfd, const struct sockaddr* serv_addr, socklen_t size){
int ret = bind(lfd, serv_addr, size);
if (ret == -1){
cout << "bind() occurr fail " << strerror(errno) << endl;
exit(1);
}
}
void SelectTcp::Listen(int lfd, int backlog){
int ret = listen(lfd, backlog);
if (ret == -1){
cout << "listen() occurr fail " << strerror(errno) << endl;
exit(1);
}
}
int SelectTcp::Accept(int lfd, struct sockaddr* cli_addr, socklen_t* cli_addr_len){
int ret = accept(lfd, cli_addr, cli_addr_len);
if (ret == -1){
cout << "accept() occurr fail " << strerror(errno) << endl;
exit(1);
}
return ret;
}
int SelectTcp::Select(int nfds, fd_set* readfds, fd_set* writefds, fd_set*exceptfds, struct timeval* timeout){
int ret = select(nfds, readfds, writefds, exceptfds, timeout);
if (ret == -1){
cout << "select() occurr fail " << strerror(errno) << endl;
exit(1);
}
return ret;
}
int SelectTcp::Read(int fd, void* buf, size_t size, fd_set& set){
int ret = read(fd, buf, size);
if (ret == 0){
close(fd);
FD_CLR(fd, &set);
} else if (ret > 0){
return ret;
} else if (ret == -1) {
cout << "read() occurr fail " << strerror(errno) << endl;
exit(1);
}
}
int SelectTcp::Write(int fd, const void* buf, size_t size){
int ret = write(fd, buf, size);
if (ret == -1){
cout << "write() occurr fail " << strerror(errno) << endl;
exit(1);
}
return ret;
}
#endif
#include "selectTcp.h"
#include<ctype.h>
int initSocketConnect(SelectTcp& tcp, struct sockaddr_in& serv_addr){
int opt = 1;
int lfd = tcp.Socket(AF_INET, SOCK_STREAM, 0);
tcp.Setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
tcp.setAddr(serv_addr, 1);
tcp.Bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
tcp.Listen(lfd, 128);
return lfd;
}
void initSelectSet(fd_set& aset, int lfd){
FD_ZERO(&aset);
FD_SET(lfd, &aset);
}
int do_accept(int lfd, SelectTcp& tcp){
}
void do_read(int cfd){
}
int main(){
struct sockaddr_in serv_addr, cli_addr;
socklen_t cli_addr_len;
int n, ret, maxfd, cfd;
char buf[BUFSIZ];
fd_set readSet, allSet;
SelectTcp tcp("the server is ready!!!");
int lfd = initSocketConnect(tcp, serv_addr); //初始化阻塞监听前的信息;;;
initSelectSet(allSet, lfd); //初始化select之前的信息;
maxfd = lfd;
cout << "the server start listening the connection!!!!" << endl;
while(1){
cli_addr_len = sizeof(cli_addr);
readSet = allSet;
n = tcp.Select(maxfd + 1, &readSet, NULL, NULL, NULL);
if (FD_ISSET(lfd, &readSet)){
//cfd = do_accept(lfd, tcp_select);
cfd = tcp.Accept(lfd, (struct sockaddr*)&cli_addr, &cli_addr_len);
FD_SET(cfd, &allSet);
if (cfd > maxfd)maxfd = cfd;
if (n == 1){
continue;
}
}
for (int i = lfd; i <= maxfd; i ++){
if (FD_ISSET(i, &readSet)){
//do_read(i);
ret = tcp.Read(i, buf, sizeof(buf), allSet);
if (ret > 0){
for (int j = 0; j < ret; j ++){
buf[j] = toupper(buf[j]);
}
tcp.Write(STDOUT_FILENO, buf, ret);
tcp.Write(i, buf, ret);
}
}
}
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。