dyad是一个基于C编写的异步网络库,非常精简,单C文件,仅实现TCP,很适合用来学习Linux网络编程和异步非阻塞处理
Github链接
我写了一些注释
#include
#include
#include
#include "dyad.h"
/* An echo server: Echos any data received by a client back to the client */
static void onData(dyad_Event *e) {
dyad_write(e->stream, e->data, e->size);
}
static void onAccept(dyad_Event *e) {
dyad_addListener(e->remote, DYAD_EVENT_DATA, onData, NULL);
dyad_writef(e->remote, "echo server\r\n");
}
static void onError(dyad_Event *e) {
printf("server error: %s\n", e->msg);
}
int main(void) {
dyad_Stream *s;
dyad_init();//初始化dyad
s = dyad_newStream();//新建一个流
dyad_addListener(s, DYAD_EVENT_ERROR, onError, NULL);//添加监听
dyad_addListener(s, DYAD_EVENT_ACCEPT, onAccept, NULL);//添加监听
dyad_listen(s, 8000);//监听8000端口
while (dyad_getStreamCount() > 0) {
dyad_update();//更新
}
return 0;
}
整体实现是基于select事件链表的方式(也就是它定义的stream)实现的,核心思想还是链表,所以分析也是从它的基础结构dyad_Stream结构体开始,我会试着分析下echo server实现中涉及到的函数.
struct dyad_Stream {
int state, flags;//状态标志
dyad_Socket sockfd;//文件描述符
char *address;//地址
int port;//端口号
int bytesSent, bytesReceived;//字节发送与接收
double lastActivity, timeout;//上一次的活动时间,超时时间
Vec(Listener) listeners;//listeners链表
Vec(char) lineBuffer;//lineBuffer链表
Vec(char) writeBuffer;//writeBuffer链表
dyad_Stream *next;//链表结构
};
作者是这样定义TCP流的,可以看到该结构是存在链表数据结构的next指针,指向下一个dyad_Stream
Vec是实现模板动态数组的一组宏,应该说是这个网络库的核心数据结构了,可以看到这个“模板动态数组”是支持任意数据结构的。
static void vec_expand(char **data, int *length, int *capacity, int memsz) {
if (*length + 1 > *capacity) {
if (*capacity == 0) {
*capacity = 1;
} else {
*capacity <<= 1;
}
*data = dyad_realloc(*data, *capacity * memsz);
}
}
static void vec_splice(
char **data, int *length, int *capacity, int memsz, int start, int count
) {
(void) capacity;
memmove(*data + start * memsz,
*data + (start + count) * memsz,
(*length - start - count) * memsz);
}
#define Vec(T)\
struct { T *data; int length, capacity; }
#define vec_unpack(v)\
(char**)&(v)->data, &(v)->length, &(v)->capacity, sizeof(*(v)->data)
#define vec_init(v)\
memset((v), 0, sizeof(*(v)))
#define vec_deinit(v)\
dyad_free((v)->data)
//清除长度
#define vec_clear(v)\
((v)->length = 0)
#define vec_push(v, val)\
( vec_expand(vec_unpack(v)),\
(v)->data[(v)->length++] = (val) )
//分割
#define vec_splice(v, start, count)\
( vec_splice(vec_unpack(v), start, count),\
(v)->length -= (count) )
这里我举一个例子(就下面这句),简单分析下它是如何实现入队列和扩容的
Listener listener;
vec_push(&stream->listeners, listener);
这里涉及到vec_push,也就是把listener变量压入链表中
#define vec_push(v, val)\
( vec_expand(vec_unpack(v)),\
(v)->data[(v)->length++] = (val) )
可以看到这个宏,里面又包含了vec_unpack这个宏和vec_expand这个函数,展开就是这样
( vec_expand(vec_unpack(&stream->listeners)), (&stream->listeners)->data[(&stream->listeners)->length++] = (listener) )
再展开的话
vec_expand((char**)&(&stream->listeners)->data, &(&stream->listeners)->length, &(&stream->listeners)->capacity, sizeof(*(&stream->listeners)->data));
不得不感叹,作者把宏定义玩出了花~
我们着重看vec_expand函数
static void vec_expand(char **data, int *length, int *capacity, int memsz) {
if (*length + 1 > *capacity) {//判断容量是不是超
if (*capacity == 0) {//如果容量是0
*capacity = 1;//设定容量为1
} else {
*capacity <<= 1;//将容量翻倍(右移1表示乘以2)
}
*data = dyad_realloc(*data, *capacity * memsz);//重新分配内存,扩容
}
}
void dyad_init(void) {
#ifdef _WIN32
WSADATA dat;
int err = WSAStartup(MAKEWORD(2, 2), &dat);
if (err != 0) {
panic("WSAStartup failed (%d)", err);
}
#else
/* 当对一个已经停止了的socket写操作时,停止SIGPIPE信号*/
signal(SIGPIPE, SIG_IGN);
#endif
}
#define SIG_IGN (__p_sig_fn_t)1
这个是dyad库初始化的函数
在linux下这个函数就是把SIGPIPE信号重定向到SIG_IGN(忽略)
这里我把它原来的注释翻译了下,应该是确保socket如果停止了后,如果再对其写操作,不会触发SIGPIPE
就是客户端程序向服务器端程序发送了消息,然后关闭客户端,服务器端返回消息的时候就会收到内核给的SIGPIPE信号
初始化一个新的流
static dyad_Stream *dyad_streams;//链表头
....
//新建一个流
dyad_Stream *dyad_newStream(void) {
dyad_Stream *stream = dyad_realloc(NULL, sizeof(*stream));
memset(stream, 0, sizeof(*stream));
stream->state = DYAD_STATE_CLOSED;
stream->sockfd = INVALID_SOCKET;
stream->lastActivity = dyad_getTime();
/* Add to list and increment count */
stream->next = dyad_streams;//新的元素的下一个指向链表头
dyad_streams = stream;//链表头指向新的元素
dyad_streamCount++;
return stream;
}
可以看到它将新分配的stream插入到的链表中,dyad_streams是链表头,是一个全局静态变量.
添加一个新的监听事件(^ ^就这样翻译吧)
//添加监听
void dyad_addListener(
dyad_Stream *stream, int event, dyad_Callback callback, void *udata
) {
Listener listener;
listener.event = event;
listener.callback = callback;
listener.udata = udata;
vec_push(&stream->listeners, listener);//压入链表(先扩容,然后把数据加进去)
}
将指定的事件,指定的回调函数加到流结构体的listener链表上
这个库包含的事件有
enum {
DYAD_EVENT_NULL,
DYAD_EVENT_DESTROY,
DYAD_EVENT_ACCEPT,
DYAD_EVENT_LISTEN,
DYAD_EVENT_CONNECT,
DYAD_EVENT_CLOSE,
DYAD_EVENT_READY,
DYAD_EVENT_DATA,
DYAD_EVENT_LINE,
DYAD_EVENT_ERROR,
DYAD_EVENT_TIMEOUT,
DYAD_EVENT_TICK
};
指定流的监听端口号
这个函数主要涉及到socket初始化,底层还套了一层函数dyad_listenEx
//监听
int dyad_listen(dyad_Stream *stream, int port) {
return dyad_listenEx(stream, NULL, port, 511);
}
///listen底层初始化
int dyad_listenEx(
dyad_Stream *stream, const char *host, int port, int backlog
) {
struct addrinfo hints, *ai = NULL;
int err, optval;
char buf[64];
dyad_Event e;
/* 获取地址信息 Get addrinfo */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
sprintf(buf, "%d", port);
err = getaddrinfo(host, buf, &hints, &ai);
if (err) {
stream_error(stream, "could not get addrinfo", errno);
goto fail;
}
/* 初始化socket Init socket */
err = stream_initSocket(stream, ai->ai_family, ai->ai_socktype,
ai->ai_protocol);
if (err) goto fail;
/* Set SO_REUSEADDR so that the socket can be immediately bound without
* having to wait for any closed socket on the same port to timeout */
optval = 1;
setsockopt(stream->sockfd, SOL_SOCKET, SO_REUSEADDR,
&optval, sizeof(optval));
/* Bind and listen */
err = bind(stream->sockfd, ai->ai_addr, ai->ai_addrlen);
if (err) {
stream_error(stream, "could not bind socket", errno);
goto fail;
}
err = listen(stream->sockfd, backlog);
if (err) {
stream_error(stream, "socket failed on listen", errno);
goto fail;
}
stream->state = DYAD_STATE_LISTENING;//修改流状态
stream->port = port;
stream_initAddress(stream);
/* Emit listening event */
e = createEvent(DYAD_EVENT_LISTEN);//创建事件
e.msg = "socket is listening";
stream_emitEvent(stream, &e);//触发事件
freeaddrinfo(ai);
return 0;
fail:
if (ai) freeaddrinfo(ai);
return -1;
}
这个函数主要干了这些事,我这里不贴其他涉及的函数了,比较多
获取流的总数,这个没啥好说的
这个库的核心函数,着重分析这个函数
这个函数就是用来处理整个事件触发机制的
///更新
void dyad_update(void) {
dyad_Stream *stream;
struct timeval tv;
destroyClosedStreams();//销毁掉已经关掉的流
updateTickTimer();//更新计时器
updateStreamTimeouts();//更新流的超时计时器
/* Create fd sets for select() */
select_zero(&dyad_selectSet);
//遍历每一个流链表的子元素,根据TCP状态处理select事件
stream = dyad_streams;
while (stream) {
switch (stream->state) {
case DYAD_STATE_CONNECTED:
select_add(&dyad_selectSet, SELECT_READ, stream->sockfd);
if (!(stream->flags & DYAD_FLAG_READY) ||
stream->writeBuffer.length != 0
) {
select_add(&dyad_selectSet, SELECT_WRITE, stream->sockfd);
}
break;
case DYAD_STATE_CLOSING:
select_add(&dyad_selectSet, SELECT_WRITE, stream->sockfd);
break;
case DYAD_STATE_CONNECTING:
select_add(&dyad_selectSet, SELECT_WRITE, stream->sockfd);
select_add(&dyad_selectSet, SELECT_EXCEPT, stream->sockfd);
break;
case DYAD_STATE_LISTENING:
select_add(&dyad_selectSet, SELECT_READ, stream->sockfd);
break;
}
stream = stream->next;
}
/* Init timeout value and do select */
#ifdef _MSC_VER
#pragma warning(push)
/* Disable double to long implicit conversion warning,
* because the type of timeval's fields don't agree across platforms */
#pragma warning(disable: 4244)
#endif
tv.tv_sec = dyad_updateTimeout;
tv.tv_usec = (dyad_updateTimeout - tv.tv_sec) * 1e6;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
//监视文件描述符的读/写/异常变化
select(dyad_selectSet.maxfd + 1,
dyad_selectSet.fds[SELECT_READ],
dyad_selectSet.fds[SELECT_WRITE],
dyad_selectSet.fds[SELECT_EXCEPT],
&tv);
/* Handle streams */
//遍历每一个流链表的子元素,根据select的状态做事情
stream = dyad_streams;
while (stream) {
switch (stream->state) {
case DYAD_STATE_CONNECTED://连接上
if (select_has(&dyad_selectSet, SELECT_READ, stream->sockfd)) {
stream_handleReceivedData(stream);
if (stream->state == DYAD_STATE_CLOSED) {
break;
}
}
/* Fall through */
case DYAD_STATE_CLOSING://关闭
if (select_has(&dyad_selectSet, SELECT_WRITE, stream->sockfd)) {
stream_flushWriteBuffer(stream);
}
break;
case DYAD_STATE_CONNECTING://正在连接
if (select_has(&dyad_selectSet, SELECT_WRITE, stream->sockfd)) {
/* Check socket for error */
int optval = 0;
socklen_t optlen = sizeof(optval);
dyad_Event e;
getsockopt(stream->sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen);
if (optval != 0) goto connectFailed;//判断有没有socket错误
/* Handle succeselful connection */
stream->state = DYAD_STATE_CONNECTED;
stream->lastActivity = dyad_getTime();
stream_initAddress(stream);
/* Emit connect event */
e = createEvent(DYAD_EVENT_CONNECT);
e.msg = "connected to server";
stream_emitEvent(stream, &e);
} else if (
select_has(&dyad_selectSet, SELECT_EXCEPT, stream->sockfd)
) {
/* Handle failed connection */
connectFailed:
stream_error(stream, "could not connect to server", 0);
}
break;
case DYAD_STATE_LISTENING://正在监听
if (select_has(&dyad_selectSet, SELECT_READ, stream->sockfd)) {
stream_acceptPendingConnections(stream);
}
break;
}
/* If data was just now written to the stream we should immediately try to
* send it */
if (
stream->flags & DYAD_FLAG_WRITTEN &&
stream->state != DYAD_STATE_CLOSED
) {
stream_flushWriteBuffer(stream);
}
stream = stream->next;
}
}
在分析它之前,我们先看看SelectSet结构体,因为这个和后面的select有很大的关系
enum {
SELECT_READ,
SELECT_WRITE,
SELECT_EXCEPT,
SELECT_MAX
};
//Select组
typedef struct {
int capacity;
dyad_Socket maxfd;
fd_set *fds[SELECT_MAX];
} SelectSet;
#define DYAD_UNSIGNED_BIT (sizeof(unsigned) * CHAR_BIT)
static void select_deinit(SelectSet *s) {
int i;
for (i = 0; i < SELECT_MAX; i++) {
dyad_free(s->fds[i]);
s->fds[i] = NULL;
}
s->capacity = 0;
}
//select组扩容
static void select_grow(SelectSet *s) {
int i;
int oldCapacity = s->capacity;
s->capacity = s->capacity ? s->capacity << 1 : 1;
for (i = 0; i < SELECT_MAX; i++) {
s->fds[i] = dyad_realloc(s->fds[i], s->capacity * sizeof(fd_set));
memset(s->fds[i] + oldCapacity, 0,
(s->capacity - oldCapacity) * sizeof(fd_set));
}
}
static void select_zero(SelectSet *s) {
int i;
if (s->capacity == 0) return;
s->maxfd = 0;
for (i = 0; i < SELECT_MAX; i++) {
#if _WIN32
s->fds[i]->fd_count = 0;
#else
memset(s->fds[i], 0, s->capacity * sizeof(fd_set));
#endif
}
}
//向select组添加select
static void select_add(SelectSet *s, int set, dyad_Socket fd) {
#ifdef _WIN32
fd_set *f;
if (s->capacity == 0) select_grow(s);
while ((unsigned) (s->capacity * FD_SETSIZE) < s->fds[set]->fd_count + 1) {
select_grow(s);
}
f = s->fds[set];
f->fd_array[f->fd_count++] = fd;
#else
unsigned *p;
while (s->capacity * FD_SETSIZE < fd) {
select_grow(s);//select组扩容
}
p = (unsigned*) s->fds[set];
p[fd / DYAD_UNSIGNED_BIT] |= 1 << (fd % DYAD_UNSIGNED_BIT);//除以DYAD_UNSIGNED_BIT和% DYAD_UNSIGNED_BIT是为了防止数组越界
if (fd > s->maxfd) s->maxfd = fd;
#endif
}
//判断指定的selectset有没有对应的事件发生
static int select_has(SelectSet *s, int set, dyad_Socket fd) {
#ifdef _WIN32
unsigned i;
fd_set *f;
if (s->capacity == 0) return 0;
f = s->fds[set];
for (i = 0; i < f->fd_count; i++) {
if (f->fd_array[i] == fd) {
return 1;
}
}
return 0;
#else
unsigned *p;
if (s->maxfd < fd) return 0;
p = (unsigned*) s->fds[set];
return p[fd / DYAD_UNSIGNED_BIT] & (1 << (fd % DYAD_UNSIGNED_BIT));
#endif
}
涉及到的代码先贴上
结构体的结构如上图示
后面要用select函数来找我们感兴趣的文件描述符,select函数的参数如下
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
可以看到需要3个fd_set,分别对应感兴趣的读/写/异常的文件描述符集合
那这个文件描述符集合又是啥
其实这个东西就是一个64位的数,每一个位对应一个文件描述符,如果这一位为1说明对应的文件描述符发生了指定的事[读/写/异常],就是位图
为了实现对这些位图的操作,他提供了上面这些函数,包括赋值/清空/判断/初始化/释放操作函数。
其中我这里重点说下select_add
函数原型是
static void select_add(SelectSet *s, int set, dyad_Socket fd)
static void select_add(SelectSet *s, int set, dyad_Socket fd) {
#ifdef _WIN32
fd_set *f;
if (s->capacity == 0) select_grow(s);
while ((unsigned) (s->capacity * FD_SETSIZE) < s->fds[set]->fd_count + 1) {
select_grow(s);
}
f = s->fds[set];
f->fd_array[f->fd_count++] = fd;
#else
unsigned *p;
while (s->capacity * FD_SETSIZE < fd) {
select_grow(s);//select组扩容
}
p = (unsigned*) s->fds[set];
p[fd / DYAD_UNSIGNED_BIT] |= 1 << (fd % DYAD_UNSIGNED_BIT);//除以DYAD_UNSIGNED_BIT和% DYAD_UNSIGNED_BIT是为了防止数组越界
if (fd > s->maxfd) s->maxfd = fd;
#endif
}
流程图如下
把这个弄明白,就可以来分析逐个函数update函数的实现了
销毁掉已经关掉的流
static void destroyClosedStreams(void) {
dyad_Stream *stream = dyad_streams;
while (stream) {
if (stream->state == DYAD_STATE_CLOSED) {
dyad_Stream *next = stream->next;
stream_destroy(stream);//把需要删掉的流从流链表里销毁
stream = next;//把后一个替补上去
} else {
stream = stream->next;
}
}
}
这个函数的作用就是将需要删除的流(已经关闭的流)移出链表,然后把后面的元素接上。
移出链表的实现函数在stream_destroy
关闭socket,将待移除的元素移出链表之后,触发销毁流的事件,释放掉分配的内存。
//销毁流
static void stream_destroy(dyad_Stream *stream) {
dyad_Event e;
dyad_Stream **next;
/* Close socket */
if (stream->sockfd != INVALID_SOCKET) {
close(stream->sockfd);
}
/* Emit destroy event */
e = createEvent(DYAD_EVENT_DESTROY);
e.msg = "the stream has been destroyed";
stream_emitEvent(stream, &e);
/* 从链表中删掉这个元素 Remove from list and decrement count */
next = &dyad_streams;
while (*next != stream) {
next = &(*next)->next;
}
*next = stream->next;
dyad_streamCount--;
/* Destroy and free */
vec_deinit(&stream->listeners);
vec_deinit(&stream->lineBuffer);
vec_deinit(&stream->writeBuffer);
dyad_free(stream->address);
dyad_free(stream);
}
更新计时器
每一个流都有一个时间计数器和超时计数器
这个函数就是用来维护时间计数器这个计时变量的,每次更新时间之后,触发更新时间的事件
static void updateTickTimer(void) {
/* Update tick timer */
if (dyad_lastTick == 0) {
dyad_lastTick = dyad_getTime();
}
while (dyad_lastTick < dyad_getTime()) {
/* 发射给所有流 Emit event on all streams */
dyad_Stream *stream;
dyad_Event e = createEvent(DYAD_EVENT_TICK);
e.msg = "a tick has occured";
stream = dyad_streams;
while (stream) {
stream_emitEvent(stream, &e);
stream = stream->next;
}
dyad_lastTick += dyad_tickInterval;//更新时间戳
}
}
更新流的超时计时器
这个函数就是用来维护超时计时器这个计时变量的,每次更新时间之后,触发时间超时的事件
///更新流的超时计数器
static void updateStreamTimeouts(void) {
double currentTime = dyad_getTime();
dyad_Stream *stream;
dyad_Event e = createEvent(DYAD_EVENT_TIMEOUT);
e.msg = "stream timed out";
stream = dyad_streams;
while (stream) {
if (stream->timeout) {
if (currentTime - stream->lastActivity > stream->timeout) {
stream_emitEvent(stream, &e);
dyad_close(stream);
}
}
stream = stream->next;
}
}
点开大图更清晰
画出来搞明白了,就是前半部分switch-case注册select事件,select函数之后,后半部分switch-case处理各个状态下事件发生后的具体处理。
在这个库的框架下,事件也是一个重要的概念
当用户调用dyad_addListener添加需要关注的事件的时候,其实就是向对应的流注册事件,将事件挂到对应的流上。
事件的结构体定义如下:
typedef struct {
int type;//类型
void *udata;//用户数据
dyad_Stream *stream;//对应的流
dyad_Stream *remote;//对应远端的流
const char *msg;//消息
char *data;//数据
int size;//大小
} dyad_Event;
库提供了函数用于触发事件
static void stream_emitEvent(dyad_Stream *stream, dyad_Event *e) {
int i;
e->stream = stream;
for (i = 0; i < stream->listeners.length; i++) {
Listener *listener = &stream->listeners.data[i];
if (listener->event == e->type) {//被触发的事件的类型 == 流中注册的事件类型
e->udata = listener->udata;
listener->callback(e);//回调函数
}
/* Check to see if this listener was removed: If it was we decrement `i`
* since the next listener will now be in this ones place */
if (listener != &stream->listeners.data[i]) {
i--;
}
}
}
Update函数里还有一类重要的函数,事件处理子函数
这里我重要读了stream_handleReceivedData,stream_acceptPendingConnections和stream_flushWriteBuffer
处理数据的接收
//处理接收到的数据
static void stream_handleReceivedData(dyad_Stream *stream) {
for (;;) {
/* Receive data */
dyad_Event e;
char data[8192];
int size = recv(stream->sockfd, data, sizeof(data) - 1, 0);
if (size <= 0) {
if (size == 0 || errno != EWOULDBLOCK) {
/* Handle disconnect */
dyad_close(stream);
return;
} else {
/* No more data */
return;
}
}
data[size] = 0;
/* Update status */
stream->bytesReceived += size;
stream->lastActivity = dyad_getTime();
/* Emit data event */
e = createEvent(DYAD_EVENT_DATA);
e.msg = "received data";
e.data = data;
e.size = size;
stream_emitEvent(stream, &e);
/* Check stream state in case it was closed during one of the data event
* handlers. */
if (stream->state != DYAD_STATE_CONNECTED) {
return;
}
/* 处理线事件 Handle line event */
if (stream_hasListenerForEvent(stream, DYAD_EVENT_LINE)) {
int i, start;
char *buf;
for (i = 0; i < size; i++) {
vec_push(&stream->lineBuffer, data[i]);//逐字节压入lineBuffer
}
start = 0;
buf = stream->lineBuffer.data;
for (i = 0; i < stream->lineBuffer.length; i++) {
if (buf[i] == '\n') {
dyad_Event e;
buf[i] = '\0';
e = createEvent(DYAD_EVENT_LINE);
e.msg = "received line";
e.data = &buf[start];
e.size = i - start;
/* Check and strip carriage return */
if (e.size > 0 && e.data[e.size - 1] == '\r') {
e.data[--e.size] = '\0';//字符串分段处理
}
stream_emitEvent(stream, &e);//触发事件
start = i + 1;
/* Check stream state in case it was closed during one of the line
* event handlers. */
if (stream->state != DYAD_STATE_CONNECTED) {
return;
}
}
}
if (start == stream->lineBuffer.length) {
vec_clear(&stream->lineBuffer);
} else {
vec_splice(&stream->lineBuffer, 0, start);
}
}
}
}
可以看到函数先是调用了recv函数,接收数据,然后将接收到的数据挂载到流上,并触发数据接收事件。下半部分,作者设计了一个Line事件(可以理解为行事件嘛),专门处理字符串吧
作者将接收到的数据逐个字节压入流的lineBuffer里,然后也是创建对应的“行事件”,触发“行事件”
static void stream_acceptPendingConnections(dyad_Stream *stream) {
for (;;) {
dyad_Stream *remote;
dyad_Event e;
int err = 0;
dyad_Socket sockfd = accept(stream->sockfd, NULL, NULL);
if (sockfd == INVALID_SOCKET) {
err = errno;
if (err == EWOULDBLOCK) {
/* No more waiting sockets */
return;
}
}
/* Create client stream */
remote = dyad_newStream();
remote->state = DYAD_STATE_CONNECTED;
/* Set stream's socket */
stream_setSocket(remote, sockfd);
/* Emit accept event */
e = createEvent(DYAD_EVENT_ACCEPT);
e.msg = "accepted connection";
e.remote = remote;
stream_emitEvent(stream, &e);
/* Handle invalid socket -- the stream is still made and the ACCEPT event
* is still emitted, but its shut immediately with an error */
if (remote->sockfd == INVALID_SOCKET) {
stream_error(remote, "failed to create socket on accept", err);
return;
}
}
}
这个函数是处理accept的,可以看到调用了accept,如果accept成功,则创建accept事件,触发事件,执行accept的回调函数。
//处理写缓存
static int stream_flushWriteBuffer(dyad_Stream *stream) {
stream->flags &= ~DYAD_FLAG_WRITTEN;
if (stream->writeBuffer.length > 0) {
/* Send data */
int size = send(stream->sockfd, stream->writeBuffer.data,
stream->writeBuffer.length, 0);
if (size <= 0) {
if (errno == EWOULDBLOCK) {
/* No more data can be written */
return 0;
} else {
/* Handle disconnect */
dyad_close(stream);
return 0;
}
}
if (size == stream->writeBuffer.length) {
vec_clear(&stream->writeBuffer);
} else {
vec_splice(&stream->writeBuffer, 0, size);
}
/* Update status */
stream->bytesSent += size;
stream->lastActivity = dyad_getTime();
}
if (stream->writeBuffer.length == 0) {
dyad_Event e;
/* If this is a 'closing' stream we can properly close it now */
if (stream->state == DYAD_STATE_CLOSING) {
dyad_close(stream);
return 0;
}
/* Set ready flag and emit 'ready for data' event */
stream->flags |= DYAD_FLAG_READY;
e = createEvent(DYAD_EVENT_READY);
e.msg = "stream is ready for more data";
stream_emitEvent(stream, &e);
}
/* Return 1 to indicate that more data can immediately be written to the
* stream's socket */
return 1;
}
这个函数是处理写操作的函数,调用了send函数,在处理完send之后,判断是否空闲下来(待发送数据空)来决定是不是需要触发ready事件,执行对应的回调函数。
大部分的核心功能差不多就分析完了,对我这个刚刚入门写Linux程序的菜鸡来说,真是学到了不少东西。