0
点赞
收藏
分享

微信扫一扫

计算机网络-第7章 网络安全(1)

boom莎卡拉卡 2024-03-17 阅读 8

TCP并发模型:

1. TCP多线程模型:

    缺点:创建线程会带来资源开销,能够实现的并发量比较有限。

2. IO模型:

    1. 阻塞IO:

        没有数据到来时,可以让任务挂起,节省CPU资源开销,提高系统效率

    2. 非阻塞IO:

        程序未接收到数据时一直执行,效率很低

        举例应用:

write.c

#include "head.h"

int main(void)
{
int fd = 0;
char tmpbuff[1024] = {0};

mkfifo("/tmp/myfifo", 0664);
fd = open("/tmp/myfifo", O_WRONLY);
if(fd == -1)
{
perror("fail to open");
return -1;
}

while(1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
gets(tmpbuff);
write(fd, tmpbuff, strlen(tmpbuff));
}

close(fd);
return 0;
}

        后面的举例应用的write.c都为上面所示

read.c

#include "head.h"

int main(void)
{
int fd = 0;
int flags = 0;
ssize_t nsize = 0;
char *pret = NULL;
char tmpbuff[1024] = {0};

mkfifo("/tmp/myfifo", 0664);
fd = open("/tmp/myfifo", O_RDONLY);
if(fd == -1)
{
perror("fail to open");
return -1;
}

flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

flags = fcntl(0, F_GETFL);
flags |= O_NONBLOCK;
fcntl(0, F_SETFL, flags);

while(1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
pret = gets(tmpbuff);
if(pret != NULL)
{
printf("STDIN:%s\n", tmpbuff);
}

memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = read(fd, tmpbuff, sizeof(tmpbuff));
if(nsize > 0)
{
printf("RECV:%s\n", tmpbuff);
}
}

close(fd);
return 0;
}

        通过fcntl将fd和stdin文件描述符设置为非阻塞IO,所以read.c既可以通过管道接收消息,也可以同终端输入,接收消息,二者不会堵塞。

    3. 异步IO:

        只能绑定一个文件描述符用来读取数据

        举例应用:

read.c

#include "head.h"

int fd = 0;

void handler(int signo)
{
char tmpbuff[1024] = {0};
ssize_t nsize = 0;

memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = read(fd, tmpbuff, sizeof(tmpbuff));
if(nsize > 0)
{
printf("RECV:%s\n", tmpbuff);
}

return;

}

int main(void)
{
int flags = 0;
ssize_t nsize = 0;
char *pret = NULL;
char tmpbuff[1024] = {0};

signal(SIGIO, handler);

mkfifo("/tmp/myfifo", 0664);
fd = open("/tmp/myfifo", O_RDONLY);
if(fd == -1)
{
perror("fail to open");
return -1;
}

flags = fcntl(fd, F_GETFL);
flags |= O_ASYNC;
fcntl(fd, F_SETFL, flags);
fcntl(fd, F_SETOWN, getpid());

while(1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
gets(tmpbuff);
printf("STDIN:%s\n", tmpbuff);

}

close(fd);
return 0;
}

    4. 多路复用:

        1. select:

            1. select监听的集合中的文件描述符有上限限制

            2. select有内核层向用户层数据空间拷贝的过程,占用系统资源开销

                原因:在使用select函数时,会涉及到内核层和用户层之间的数据传输。具体来说:select函数在内核层会监视一组文件描述符的状态变化,并在有变化发生时通知用户层。为了是实现这个功能,内核会定期检测文件描述符的状态,并在发生变化时将相关信息传递给用户层。这个过程涉及到内核层和用户层之间的数据传输,即内核层需要将监视的文件描述符的状态信息传递给用户层。这个传输过程会涉及到系统资源的开销,因为需要进行数据拷贝操作,将内核中的数据拷贝到用户空间,这个数据拷贝的过程会消耗一定的系统资源。

            3. select必须轮询检测产生事件的文件描述符

            4. select只能工作在水平触发模式(低速模式),无法工作在边沿触发模式(高速模式)

                原因:

                    1. 在水平触发模式下,一旦文件描述符中的数据可读或可写,select就会通知用户程序,并且如果这些描述符在之后的select调用中仍然保持可读或可写状态,select会再次通知程序。换句话说,在水平触发模式下,只要描述符的状态处于可读或可写状态,select就会通知程序。

                    2. 在边缘触发模式下,只有当描述符的状态发生变化时才会通知程序,这意味着如果描述符中的数据量发生变化或者有新的数据到达,程序才会被通知,而如果描述符的状态保持不变,即使在之后的select调用中它任然处于可读或可写状态,程序也不会被通知。

            举例应用:

read.c

#include "head.h"

int main(void)
{
int fd = 0;
int flags = 0;
ssize_t nsize = 0;
char *pret = NULL;
fd_set rdfds;
fd_set tmpfds;
int ret = 0;
char tmpbuff[1024] = {0};

mkfifo("/tmp/myfifo", 0664);
fd = open("/tmp/myfifo", O_RDONLY);
if(fd == -1)
{
perror("fail to open");
return -1;
}

FD_ZERO(
FD_SET(fd,
FD_SET(0,

while(1)
{
tmpfds = rdfds;
ret = select(fd+1, &tmpfds, NULL, NULL, NULL);
if(ret == -1)
{
perror("fail to select");
return -1;
}

if(FD_ISSET(0, &tmpfds))
{
memset(tmpbuff, 0, sizeof(tmpbuff));
gets(tmpbuff);
printf("STDIN:%s\n", tmpbuff);
}

if(FD_ISSET(fd, &tmpfds))
{
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("FIFO:%s\n", tmpbuff);
}
}

close(fd);
return 0;
}

        2. poll:

            1. poll有内核层向用户层数据空间拷贝的过程,占用系统资源开销

            2. poll必须轮询检测产生事件的文件描述符

            3. poll只能工作在水平触发模式(低速模式),无法工作在边沿触发模式(高速模式)

            举例应用:

read.c

#include "head.h"

int main(void)
{
int fd = 0;
int ret = 0;
int flags = 0;
ssize_t nsize = 0;
struct pollfd fds[2];
char tmpbuff[1024] = {0};


mkfifo("/tmp/myfifo", 0664);
fd = open("/tmp/myfifo", O_RDONLY);
if(fd == -1)
{
perror("fail to open");
return -1;
}

fds[0].fd = 0;
fds[0].events = POLLIN;
fds[1].fd = fd;
fds[1].events = POLLIN;

while(1)
{
ret = poll(fds, 2, -1);
if(ret == -1)
{
perror("fail to poll");
return -1;
}

if(fds[0].revents & POLLIN)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
gets(tmpbuff);
printf("STDIN:%s\n", tmpbuff);
}

if(fds[1].revents & POLLIN)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("FIFO:%s\n", tmpbuff);
}
}

close(fd);
return 0;
}

        3. epoll:

            1. epoll没有文件描述符上限限制

            2. epoll创建内核监听事件表,所以只需要在内核空间完成数据拷贝即可

            3. epoll会将产生事件的文件描述符对应的事件直接返回

            4. epoll可以工作在水平触发模式(默认:低速模式),还可以工作在边沿触发模式(高速模式) 在epoll_events的结构体中的events传入EPOLLIN | EPOLLET即可

            举例应用:

read.c

#include "head.h"

int main(void)
{
int i = 0;
int fd = 0;
int epfd = 0;
int nready = 0;
ssize_t nsize = 0;
char tmpbuff[1024] = {0};
struct epoll_event env;
struct epoll_event retenv[2];

mkfifo("/tmp/myfifo", 0664);
fd = open("/tmp/myfifo", O_RDONLY);
if(fd == -1)
{
perror("fail to open");
return -1;
}

epfd = epoll_create(2);
if(epfd == -1)
{
perror("fail to epoll_create");
return -1;
}

env.data.fd = 0;
env.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, 0,
env.data.fd = fd;
env.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd,

while(1)
{
nready = epoll_wait(epfd, retenv, 2, -1);
if(nready == -1)
{
perror("fail to epoll_wait");
return -1;
}

for(i = 0; i < nready; i++)
{
if(retenv[i].data.fd == 0)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
gets(tmpbuff);
printf("STDIN:%s\n", tmpbuff);
}
else if(retenv[i].data.fd == fd)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("FIFO:%s\n", tmpbuff);
}
}
}

close(fd);
return 0;
}

3. 函数接口:

    1. select:

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

        功能:select监听文件描述符集合中是否有文件描述编程ready状态

        参数:

            nfds:最大文件描述符的值+1

            reafds:读文件描述符集合

            writefds:写文件描述符集合

            exceptfds:其余文件描述符集合

            timeout:等待的时长

                        NULL:一直等待

        返回值:

            成功返回文件描述符集合中的文件描述符个数
            失败返回-1

    void FD_CLR(int fd, fd_set *set);

                功能:判断文件描述符fd是否仍在集合中

    void FD_SET(int fd, fd_set *set);

                功能:将文件描述符fd加入到集合中

    int  FD_ISSET(int fd, fd_set *set);

                功能:判断文件描述符fd是否仍在集合中

    void FD_ZERO(fd_set *set);

                功能:将文件描述符集合清0

    2. poll:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

        功能:监听文件描述符集合是否有事件发生

        参数:

            fds:监听文件描述符集合是否有时间发生

            nfds:监听文件描述符集合元素个数

            timeout:等待时间(-1一直等待)

struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};

                fd:监听的文件描述

                events:要监听的事件,POLLIN:是否可读,POLLOUT:是否可写

                revents:实际产生的事件

    3. epoll:

        1. epoll_create:
int epoll_create(int size);

            功能:创建一张内核事件表

            参数:

                size:事件的个数

            返回值:

                成功返回文件描述符
                失败返回-1

        2. epoll_ctl:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

            功能:维护epoll时间表

            参数:

                epfd:事件表的文件描述符

                op:

                    EPOLL_CTL_ADD     添加事件

                    EPOLL_CTL_MODE  修改事件

                    EPOLL_CTL_DEL      删除事件

                fd:操作的文件描述符

                events:事件对应的事件

typedef union epoll_data {
void *ptr;
in fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;

struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

            返回值:

                成功返回0 
                失败返回-1 

        3. epoll_wait:
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

            功能:监听事件表中的事件

            参数:

                epfd:文件描述符

                events:存放实际产生事件的数组空间首地址

                maxevents:最多存放事件的个数

                timeout:设定监听的时间(超过该时间则不再监听)

                        -1:一直监听直到有事件发生

            返回值:

                成功返回产生事件的文件描述符个数
                失败返回-1 
                如果时间达到仍没有事件发生返回0

4. 练习作业:

        1. 编写TCP并发模型之select模型

client.c

#include "head.h"

int CreateTcpClient(char *pip, int port)
{
int ret = 0;
int sockfd = 0;
struct sockaddr_in seraddr;

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("fail to socket");
return -1;
}

seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(port);
seraddr.sin_addr.s_addr = inet_addr(pip);
ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (-1 == ret)
{
perror("fail to connect");
return -1;
}

return sockfd;
}

int main(void)
{
int sockfd = 0;
char tmpbuff[4096] = {"hello world"};
int cnt = 0;
ssize_t nsize = 0;

sockfd = CreateTcpClient("192.168.1.125", 50000);

while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
sprintf(tmpbuff, "hello world --- %d", cnt);
cnt++;
nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
if (-1 == nsize)
{
perror("fail to send");
return -1;
}

memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);
if (-1 == nsize)
{
perror("fail to recv");
return -1;
}

printf("RECV:%s\n", tmpbuff);
}

close(sockfd);

return 0;
}

        后面的client.c都为上面所示

server.c

#include "head.h"

int CreateListenSocket(char *pip, int port)
{
int ret = 0;
int sockfd = 0;
struct sockaddr_in serveraddr;

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("fail to socket");
return -1;
}

serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = inet_addr(pip);
ret = bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(ret == -1)
{
perror("fail to bind");
return -1;
}


ret =listen(sockfd, 10);
if(ret == -1)
{
perror("fail to listen");
return -1;
}

return sockfd;
}

int HandleTcpClient(int confd)
{
char tmpbuff[4096] = {0};
ssize_t nsize = 0;

memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
if(nsize == -1)
{
perror("fail to recv");
return -1;
}
else if(nsize == 0)
{
return 0;
}

sprintf(tmpbuff, "%s ------echo", tmpbuff);
nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
if(nsize == -1)
{
perror("fail to send");
return -1;
}

return nsize;

}

int main(void)
{
int i = 0;
int ret = 0;
int confd = 0;
int sockfd = 0;
fd_set rdfds;
fd_set tmpfds;
int maxfd = 0;

sockfd = CreateListenSocket("192.168.1.125", 50000);

FD_ZERO(
FD_SET(sockfd,
maxfd = sockfd;

while(1)
{
tmpfds = rdfds;
ret = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
if(ret == -1)
{
perror("fail to select");
return -1;
}

if(FD_ISSET(sockfd, &tmpfds))
{
confd = accept(sockfd, NULL, NULL);
if(confd == -1)
{
perror("fail to accept");
FD_CLR(sockfd,
close(sockfd);
continue;
}

FD_SET(confd,
maxfd = maxfd > confd ? maxfd : confd;
}

for(i = sockfd+1; i <= maxfd; i++)
{
if(FD_ISSET(i, &tmpfds))
{
ret = HandleTcpClient(i);
if(ret == -1)
{
fprintf(stderr, "handle client fialed!\n");
FD_CLR(i,
close(i);
continue;
}
else if(ret == 0)
{
fprintf(stderr, "client disconnected!\n");
FD_CLR(i,
close(i);
continue;
}
}
}
}

close(confd);
close(sockfd);

return 0;
}

        2. 编写TCP并发模型之poll模型

server.c

#include "head.h"

int CreateListenSocket(char *pip, int port)
{
int ret = 0;
int sockfd = 0;
struct sockaddr_in serveraddr;

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("fail to socket");
return -1;
}

serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = inet_addr(pip);
ret = bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(ret == -1)
{
perror("fail to bind");
return -1;
}


ret =listen(sockfd, 10);
if(ret == -1)
{
perror("fail to listen");
return -1;
}

return sockfd;
}

int HandleTcpClient(int confd)
{
char tmpbuff[4096] = {0};
ssize_t nsize = 0;

memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
if(nsize == -1)
{
perror("fail to recv");
return -1;
}
else if(nsize == 0)
{
return 0;
}

sprintf(tmpbuff, "%s ------echo", tmpbuff);
nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
if(nsize == -1)
{
perror("fail to send");
return -1;
}

return nsize;

}

int InitFds(struct pollfd *fds, int maxlen)
{
int i = 0;

for(i = 0; i < maxlen; i++)
{
fds[i].fd = -1;
}

return 0;
}

int AddFds(struct pollfd *fds, int maxlen, int fd, short env)
{
int i = 0;

for(i = 0; i < maxlen; i++)
{
if(fds[i].fd == -1)
{
fds[i].fd = fd;
fds[i].events = env;
break;
}
}

if(i == maxlen)
{
return -1;
}

return 0;
}

int DeleteFds(struct pollfd *fds, int maxlen, int fd)
{
int i = 0;
for(i = 0; i < maxlen; i++)
{
if(fds[i].fd == fd)
{
fds[i].fd = -1;
break;
}
}

return 0;
}

int main(void)
{
int i = 0;
int ret = 0;
int nready = 0;
int confd = 0;
int sockfd = 0;
struct pollfd fds[1024];

sockfd = CreateListenSocket("192.168.1.125", 50000);

InitFds(fds, 1024);
AddFds(fds, 1024, sockfd, POLLIN);

while(1)
{
nready = poll(fds, 1024, -1);
if(nready == -1)
{
perror("fail to poll");
return -1;
}

for(i = 0; i < 1024; i++)
{
if(fds[i] == -1)
{
continue;
}
if(fds[i].revents & POLLIN && fds[i].fd == sockfd)
{
confd = accept(sockfd, NULL, NULL);
if(confd == -1)
{
perror("fail to accept");
close(sockfd);
DeleteFds(fds, 1024, sockfd);
continue;
}

AddFds(fds, 1024, confd, POLLIN);
}
else if(fds[i].revents & POLLIN && fds[i].fd != sockfd)
{
ret = HandleTcpClient(fds[i].fd);
if(ret == -1)
{
fprintf(stderr, "handle tcp client failed!\n");
close(fds[i].fd);
DeleteFds(fds, 1024, fds[i].fd);
continue;
}
else if(ret == 0)
{
fprintf(stderr, "client disconnected!\n");
close(fds[i].fd);
DeleteFds(fds, 1024, fds[i].fd);
continue;
}
}
}
}

close(confd);
close(sockfd);

return 0;
}

        3. 编写TCP并发模型之epoll模型

server.c

#include "head.h"

int CreateListenSocket(char *pip, int port)
{
int ret = 0;
int sockfd = 0;
struct sockaddr_in serveraddr;

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("fail to socket");
return -1;
}

serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = inet_addr(pip);
ret = bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(ret == -1)
{
perror("fail to bind");
return -1;
}


ret =listen(sockfd, 10);
if(ret == -1)
{
perror("fail to listen");
return -1;
}

return sockfd;
}

int HandleTcpClient(int confd)
{
char tmpbuff[4096] = {0};
ssize_t nsize = 0;

memset(tmpbuff, 0, sizeof(tmpbuff));
nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
if(nsize == -1)
{
perror("fail to recv");
return -1;
}
else if(nsize == 0)
{
return 0;
}

sprintf(tmpbuff, "%s ------echo", tmpbuff);
nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
if(nsize == -1)
{
perror("fail to send");
return -1;
}

return nsize;

}

int AddFd(int epfd, int fd, uint32_t env)
{
int ret = 0;
struct epoll_event tmpenv;

tmpenv.data.fd = fd;
tmpenv.events = env;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd,
if(ret == -1)
{
perror("fail to epoll_ctl");
return -1;
}

return 0;
}

int DelFd(int epfd, int fd)
{
int ret = 0;

ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
if(ret == -1)
{
perror("fail to epoll_ctl");
return -1;
}

return 0;
}


int main(void)
{
int i = 0;
int ret = 0;
int nready = 0;
int confd = 0;
int sockfd = 0;
struct epoll_event retenv[1024];
int epfd = 0;

epfd = epoll_create(1024);
if(epfd == -1)
{
perror("fail to epoll_create");
return -1;
}

sockfd = CreateListenSocket("192.168.1.155", 50000);

AddFd(epfd, sockfd, EPOLLIN);

while(1)
{
nready = epoll_wait(epfd, retenv, 1024, -1);
if(nready == -1)
{
perror("fail to epoll");
return -1;
}

for(i = 0; i < nready; i++)
{
if(retenv[i].data.fd == sockfd)
{
confd = accept(sockfd, NULL, NULL);
if(confd == -1)
{
perror("fail to accept");
DelFd(epfd, sockfd);
close(sockfd);
continue;
}

AddFd(epfd, confd, EPOLLIN);
}
else if(retenv[i].data.fd != sockfd)
{
ret = HandleTcpClient(retenv[i].data.fd);
if(ret == -1)
{
fprintf(stderr, "handle tcp client failed!\n");
DelFd(epfd, retenv[i].data.fd);
close(retenv[i].data.fd);
continue;
}
else if(ret == 0)
{
fprintf(stderr, "client disconnected!\n");
DelFd(epfd, retenv[i].data.fd);
close(retenv[i].data.fd);
continue;
}
}
}
}

close(epfd);
close(sockfd);

return 0;
}

举报

相关推荐

0 条评论