Skip to content

Instantly share code, notes, and snippets.

@onemouth
Last active January 23, 2018 08:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save onemouth/86d49a50045696d83abe1e62f53c2e63 to your computer and use it in GitHub Desktop.
Save onemouth/86d49a50045696d83abe1e62f53c2e63 to your computer and use it in GitHub Desktop.
#include <sys/socket.h>
#include <sys/event.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define exit_if(r, ...) if(r) {printf(__VA_ARGS__); printf("error no: %d error msg %s\n", errno, strerror(errno)); exit(1);}
const int kReadEvent = 1;
const int kWriteEvent = 2;
void setNonBlock(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
exit_if(flags<0, "fcntl failed");
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
exit_if(r<0, "fcntl failed");
}
void updateEvents(int efd, int fd, int events, bool modify) {
struct kevent ev[2];
int n = 0;
if (events & kReadEvent) {
EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, (void*)(intptr_t)fd);
}
if (events & kWriteEvent) {
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, (void*)(intptr_t)fd);
}
printf("%s fd %d events read %d write %d\n",
modify ? "mod" : "add", fd, events & kReadEvent, events & kWriteEvent);
int r = kevent(efd, ev, n, NULL, 0, NULL);
exit_if(r, "kevent failed ");
}
void handleAccept(int efd, int fd) {
struct sockaddr_in raddr;
socklen_t rsz = sizeof(raddr);
int cfd = accept(fd,(struct sockaddr *)&raddr,&rsz);
exit_if(cfd<0, "accept failed");
sockaddr_in peer;
socklen_t alen = sizeof(peer);
int r = getpeername(cfd, (sockaddr*)&peer, &alen);
exit_if(r<0, "getpeername failed");
printf("accept a connection from %s\n", inet_ntoa(raddr.sin_addr));
setNonBlock(cfd);
updateEvents(efd, cfd, kReadEvent|kWriteEvent, false);
}
void handleRead(int efd, int fd) {
char buf[4096];
ssize_t n = 0;
while ((n=::read(fd, buf, sizeof buf)) > 0) {
printf("read %ld bytes\n", n);
ssize_t r = ::write(fd, buf, n); //写出读取的数据
//实际应用中,写出数据可能会返回EAGAIN,此时应当监听可写事件,当可写时再把数据写出
exit_if(r<=0, "write error");
}
if (n<0 && (errno == EAGAIN || errno == EWOULDBLOCK))
return;
exit_if(n<0, "read error"); //实际应用中,n<0应当检查各类错误,如EINTR
printf("fd %d closed\n", fd);
close(fd);
}
void handleWrite(int efd, int fd) {
//实际应用应当实现可写时写出数据,无数据可写才关闭可写事件
updateEvents(efd, fd, kReadEvent, true);
}
void loop_once(int efd, int lfd, int waitms) {
struct timespec timeout;
timeout.tv_sec = waitms / 1000;
timeout.tv_nsec = (waitms % 1000) * 1000 * 1000;
const int kMaxEvents = 20;
struct kevent activeEvs[kMaxEvents];
int n = kevent(efd, NULL, 0, activeEvs, kMaxEvents, &timeout);
printf("kevent return %d\n", n);
for (int i = 0; i < n; i ++) {
int fd = (int)(intptr_t)activeEvs[i].udata;
int events = activeEvs[i].filter;
if (events == EVFILT_READ) {
if (fd == lfd) {
handleAccept(efd, fd);
} else {
handleRead(efd, fd);
}
} else if (events == EVFILT_WRITE) {
handleWrite(efd, fd);
} else {
exit_if(1, "unknown event");
}
}
}
int main(int argc, const char * argv[]) {
int port = 8787;
int kqueuefd = kqueue();
exit_if(kqueuefd < 0, "kqueue() failed");
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
int r = ::bind(listenfd,(struct sockaddr *)&addr, sizeof(struct sockaddr));
exit_if(r, "bind to 0.0.0.0:%d failed %d %s", port, errno, strerror(errno));
r = listen(listenfd, 20);
exit_if(r, "listen failed %d %s", errno, strerror(errno));
printf("fd %d listening at %d\n", listenfd, port);
setNonBlock(listenfd);
updateEvents(kqueuefd, listenfd, kReadEvent, false);
for (;;) { //实际应用应当注册信号处理函数,退出时清理资源
loop_once(kqueuefd, listenfd, 10000);
}
return 0;
}
//
// main.m
// kqueue
//
// Created by Louis Tsai (RD-TW) on 2018/1/23.
// Copyright © 2018年 Louis Tsai (RD-TW). All rights reserved.
//
#import <Foundation/Foundation.h>
#include <sys/socket.h>
#include <sys/event.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define kReadEvent 1
#define kWriteEvent 2
#define kMaxEvents 20
void setNonBlock(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
void registerListenFD(int kqfd, int listenfd){
struct kevent changes[1];
EV_SET(&changes[0], listenfd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)listenfd);
kevent(kqfd, changes, 1, NULL, 0, NULL);
}
void registerClientFD(int kqfd, int cfd){
struct kevent changes[2];
EV_SET(&changes[0], cfd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)cfd);
EV_SET(&changes[0], cfd, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)cfd);
kevent(kqfd, changes, 2, NULL, 0, NULL);
}
void handleAccept(int kqfd, int lfd){
struct sockaddr_in raddr;
socklen_t rsz = sizeof(raddr);
int cfd = accept(lfd,(struct sockaddr *)&raddr,&rsz);
struct sockaddr_in peer;
socklen_t alen = sizeof(peer);
getpeername(cfd, (struct sockaddr*)&peer, &alen);
NSLog(@"accept a connection from %s\n", inet_ntoa(raddr.sin_addr));
setNonBlock(cfd);
registerClientFD(kqfd, cfd);
}
void handleRead(int kqfd, int fd){
}
void loop_once(int kqfd, int listenfd, int waitms) {
struct timespec timeout;
timeout.tv_sec = waitms / 1000;
timeout.tv_nsec = (waitms % 1000) * 1000 * 1000;
struct kevent events[kMaxEvents];
int n = kevent(kqfd, NULL, 0, events, kMaxEvents, &timeout);
NSLog(@"kevent returns %d", n);
int i = 0;
for (i=0; i < n; i++){
int fd = (int)(intptr_t)(events[i].udata);
int event = events[i].filter;
if (event == EVFILT_READ) {
if (fd == listenfd) {
handleAccept(kqfd, fd);
} else {
handleRead(kqfd, fd);
}
} else if (event == EVFILT_WRITE) {
//handleWrite(efd, fd);
}
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int port = 8787;
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
bind(listenfd,(struct sockaddr *)&addr, sizeof(struct sockaddr));
listen(listenfd, 20);
NSLog(@"fd %d listening at %d", listenfd, port);
setNonBlock(listenfd);
int kqfd = kqueue();
registerListenFD(kqfd, listenfd);
for(;;){
loop_once(kqfd, listenfd, 10000);
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment