shttpd源码分析(3)几个重要数据结构
shttpd中为了简化处理将服务器与客户端之间的交互抽象成流(stream),流中包含建立的连接的信息、IO类型、IO操作方法(封装在io_class中)、最后读的字节的位置、头部长度、内容长度、流的当前状态标志等。
struct stream
1 /*
2 * Data exchange stream. It is backed by some communication channel:
3 * opened file, socket, etc. The 'read' and 'write' methods are
4 * determined by a communication channel.
5 */
6 struct stream {
7 struct conn *conn;
8 union channel chan; /* Descriptor */
9 struct io io; /* IO buffer */
10 const struct io_class *io_class; /* IO class */
11 int nread_last; /* Bytes last read */
12 int headers_len;
13 big_int_t content_len;
14 unsigned int flags;
15 #define FLAG_HEADERS_PARSED 1
16 #define FLAG_SSL_ACCEPTED 2
17 #define FLAG_R 4 /* Can read in general */
18 #define FLAG_W 8 /* Can write in general */
19 #define FLAG_CLOSED 16
20 #define FLAG_DONT_CLOSE 32
21 #define FLAG_ALWAYS_READY 64 /* File, dir, user_func */
22 };
23
再来看看流结构中的几个结构struct conn、union channel、struct io、const struct io_class
先看看conn 结构的定义,其中保存了conn 结构(struct llhead)的链表、shttpd上下文(struct shttpd_ctx)、封装了远程socket的结构(struct usa)、创建时间(time_t)、过期时间(time_t)、本地端口、回复状态、请求方法、解码后的uri、HTTP主版本号、HTTP此版本号、请求行、请求头部、uri中的QUERY_STRING部分、PATH_INFO、Mime类型、解析过后的头部结构(struct headers)、本地流和远程流。
代码
struct conn {
struct llhead link; /* Connections chain */
struct shttpd_ctx *ctx; /* Context this conn belongs to */
struct usa sa; /* Remote socket address */
time_t birth_time; /* Creation time */
time_t expire_time; /* Expiration time */
int loc_port; /* Local port */
int status; /* Reply status code */
int method; /* Request method */
char *uri; /* Decoded URI */
unsigned long major_version; /* Major HTTP version number */
unsigned long minor_version; /* Minor HTTP version number */
char *request; /* Request line */
char *headers; /* Request headers */
char *query; /* QUERY_STRING part of the URI */
char *path_info; /* PATH_INFO thing */
const char *mime_type; /* Mime type */
struct headers ch; /* Parsed client headers */
struct stream loc; /* Local stream */
struct stream rem; /* Remote stream */
#if !defined(NO_SSI)
void *ssi; /* SSI descriptor */
#endif /* NO_SSI */
};
接下来看看union channel,保存交流的类型的信息。比如,如果是文件,则使用channel.fd;如果是socket,则使用channel.sock
代码
/*
* The communication channel
*/
union channel {
int fd; /* Regular static file */
int sock; /* Connected socket */
struct {
int sock; /* XXX important. must be first */
SSL *ssl; /* shttpd_poll() assumes that */
} ssl; /* SSL-ed socket */
struct {
DIR *dirp;
char *path;
} dir; /* Opened directory */
struct {
void *state; /* For keeping state */
union variant func; /* User callback function */
void *data; /* User defined parameters */
} emb; /* Embedded, user callback */
};
接着看看io结构,这个主要是保存io操作的一些状态信息
/*
* I/O buffer descriptor
*/
struct io {
char *buf; /* IO Buffer */
size_t size; /* IO buffer size */
size_t head; /* Bytes read */
size_t tail; /* Bytes written */
size_t total; /* Total bytes read */
};
再来看看io_class的结构,包括IO类别的名称、此类IO的读写和关闭操作的函数指针。
这里使用函数指针的好处是提供统一的接口调用,而可以在实际调用的时候使用不同的实现。这就类似于c++中的多态吧,只是这里用c的方式来实现。
/*
* IO class descriptor (file, directory, socket, SSL, CGI, etc)
* These classes are defined in io_*.c files.
*/
struct io_class {
const char *name;
int (*read)(struct stream *, void *buf, size_t len);
int (*write)(struct stream *, const void *buf, size_t len);
void (*close)(struct stream *);
};
例如在io_socket.c中定义的io_scoket
const struct io_class io_socket = {
"socket",
read_socket,
write_socket,
close_socket
};
然后在io_socket.c中是实现了read_socket、write_socket、close_socket来完成读写和关闭socket的操作
浙公网安备 33010602011771号