beanstalk把监听socket加入了epoll,当客户端有连接时,在主循环sockmain中通过处理epoll事件完成对连接的处理。
1. 客户端连接处理代码
通过前面对主循环的分析可以看出,监听socket的回调处理函数为srvaccept(见如下函数)。
通过分析h_accept可以看出,客户端连接的处理包括三个步骤:
a.建立连接socket,并将连接设置为非阻塞;
b.创建连接管理对象,
c.将和客户端的连接加入epoll
连接处理#监听服务回调函数,用作接受连接
void
srvaccept(Server *s, int ev)
{
h_accept(s->sock.fd, ev, s);
}
void
h_accept(const int fd, const short which, Server *s)
{
Conn *c;
int cfd, flags, r;
socklen_t addrlen;
struct sockaddr_in6 addr;
addrlen = sizeof addr;
cfd = accept(fd, (struct sockaddr *)&addr, &addrlen);
if (cfd == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) twarn("accept()");
update_conns();
return;
}
if (verbose) {
printf("accept %d\n", cfd);
}
flags = fcntl(cfd, F_GETFL, 0);
if (flags < 0) {
twarn("getting flags");
close(cfd);
if (verbose) {
printf("close %d\n", cfd);
}
update_conns();
return;
}
r = fcntl(cfd, F_SETFL, flags | O_NONBLOCK);
if (r < 0) {
twarn("setting O_NONBLOCK");
close(cfd);
if (verbose) {
printf("close %d\n", cfd);
}
update_conns();
return;
}
//创建连接,并将状态设置为获取用户的请求命令
c = make_conn(cfd, STATE_WANTCOMMAND, default_tube, default_tube);
if (!c) {
twarnx("make_conn() failed");
close(cfd);
if (verbose) {
printf("close %d\n", cfd);
}
update_conns();
return;
}
c->srv = s;
c->sock.x = c; //回调参数,连接信息
c->sock.f = (Handle)prothandle; //回调函数,协议处理
c->sock.fd = cfd; //和客户端连接的socket句柄
//将连接socket加入epoll,设置为读取用户请求命令
r = sockwant(&c->sock, 'r');
if (r == -1) {
twarn("sockwant");
close(cfd);
if (verbose) {
printf("close %d\n", cfd);
}
update_conns();
return;
}
update_conns();
}
2. 客户端连接管理
每一个连接抽象为如下结构。
连接抽象struct Conn {
Server *srv; //连接所属的Server
Socket sock; //连接socket
char state; //连接状态
char type;
Conn *next;
tube use;
int64 tickat; // time at which to do more work
int tickpos; // position in srv->conns
job soonest_job; // memoization of the soonest job
int rw; // currently want: 'r', 'w', or 'h'
int pending_timeout;
char cmd[LINE_BUF_SIZE]; // this string is NOT NUL-terminated
int cmd_len;
int cmd_read;
char *reply;
int reply_len;
int reply_sent;
char reply_buf[LINE_BUF_SIZE]; // this string IS NUL-terminated
// How many bytes of in_job->body have been read so far. If in_job is NULL
// while in_job_read is nonzero, we are in bit bucket mode and
// in_job_read's meaning is inverted -- then it counts the bytes that
// remain to be thrown away.
int in_job_read;
job in_job; // a job to be read from the client
job out_job;
int out_job_sent;
struct ms watch; //watch的tube
struct job reserved_jobs; // linked list header
};
注意这里的连接socket和监听socket都抽象为统一的Socket类型,两者的差别是属性值。
监听socket的回调参数是Server,回调函数是h_accept,而连接socket的回调参数是Conn,回调函数是prothandle。