实现简单的netcat,IO复用,视频p21
本笔记参考的视频链接:https://www.bilibili.com/video/BV1Ht411p7wx?p=21 库链接:https://github.com/chenshuo/muduo
##IO模式:阻塞和非阻塞 关于阻塞和非阻塞在另一个文章来介绍
epoll简介
在这之前先了解一下epoll:epoll 全称 eventpoll,是 linux 内核实现IO多路复用(IO multiplexing)的一个实现。IO多路复用的意思是在一个操作里同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其的进行读写操作,和epoll类似的还有poll、select;epoll 监听的 fd(file descriptor)集合是常驻内核的,它有 3 个系统调用 (epoll_create, epoll_wait, epoll_ctl),通过 epoll_wait 可以多次监听同一个 fd 集合,只返回可读写那部分。更详细的介绍在另一个文章
##编译下面的程序
代码语言:javascript复制g io_multi.cc -o io_multi
##服务端运行
代码语言:javascript复制./io_multi -l 12345
##服务端运行
代码语言:javascript复制./io_multi 192.168.200.134 12345
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <error.h>
#include <strings.h>
#include <sys/types.h>
#include <pthread.h>
#include <fcntl.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <linux/tcp.h>
#include <signal.h>
#include <arpa/inet.h>
#define MAX 8192
#define SIZE 1024
int setnonblocking(int fd)
{
int old = fcntl(fd, F_GETFL);
int new_one = old | O_NONBLOCK;
if (fcntl(fd, F_SETFL, new_one) < 0)
{
perror("FCNTL : ");
return -1;
}
return old;
}
void addfd(int epollfd, int fd, bool enable_et)
{
struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN;
if (enable_et)
{
ev.events |= EPOLLET;
}
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
//setnonblocking(fd);
}
void run(int sockfd)
{
struct epoll_event events[SIZE];
int epollfd = epoll_create1(0);
assert(epollfd != -1);
addfd(epollfd, sockfd, false);
addfd(epollfd, STDIN_FILENO, false);
char buf[MAX];
bool done = false;
while (!done)
{
int ret = epoll_wait(epollfd, events, SIZE, -1);
for (int i=0; i<ret; i)
{
int fd = events[i].data.fd;
if (fd == STDIN_FILENO)
{
int nr = read(STDIN_FILENO, buf, sizeof(buf));
if (nr > 0)
send(sockfd, buf, nr, 0);
else
{
//fix me
close(fd);
//shutdown(write)
//unrigister stdin
}
}
else if (fd == sockfd)
{
int nr = recv(sockfd, buf, sizeof(buf), 0);
if (nr > 0)
write(STDOUT_FILENO, buf, nr);
else
done = true;
}
}
}
}
int main(int argc, char **argv)
{
//sigpipe
signal(SIGPIPE, SIG_IGN);
if (argc < 3)
{
printf("Usage:n %s hostname portn %s -l portn", argv[0], argv[0]);
return 0;
}
int port = atoi(argv[2]);
if (strcmp(argv[1], "-l") == 0)
{
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);
//address reuse and no nagle
int reuse = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
setsockopt(listenfd, IPPROTO_TCP, TCP_NODELAY, &reuse, sizeof(reuse));
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret >= 0);
ret = listen(listenfd,5);
assert(ret >= 0);
//printf("Listening...n");
struct sockaddr_in cli;
int len = sizeof(cli);
int sockfd = accept(listenfd, (struct sockaddr*)&cli, (socklen_t*)&len);
run(sockfd);
}
else
{
int sockfd;
struct sockaddr_in ser;
bzero(&ser,sizeof(ser));
sockfd = socket(AF_INET,SOCK_STREAM,0);
//server
ser.sin_family = AF_INET;
ser.sin_addr.s_addr = inet_addr(argv[1]);
ser.sin_port = htons(12345);
connect(sockfd, (struct sockaddr*)&ser, sizeof(ser));
run(sockfd);
}
return 0;
}