问题代码如下,当时我是想用libevent在win32上监听一个端口的输入。
/* * Compile with: * cc -I/usr/local/include -o event-test event-test.c -L/usr/local/lib -levent * see * http://d.hatena.ne.jp/mtaneda/20090302/1235998584 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <sys/types.h> #include <sys/stat.h> #ifndef WIN32 #include <sys/queue.h> #include <unistd.h> #include <sys/time.h> #else #include <winsock.h> #include <windows.h> #endif #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <event.h> static void read_handler(int fd, short event, void *arg) { char buf[256] = {0}; struct event *ev; ZeroMemory(buf, sizeof(buf)); ev = (struct event*)arg; if(event & EV_READ) { if(recv(fd, buf, sizeof(buf), 0)<=0) { event_del(ev); free(ev); closesocket(fd); } else { printf("[read_handler] read=%s\n", buf); } } } static void accept_handler(int fd, short event, void *arg) { struct sockaddr_in addr; struct event *new_ev; int new_fd; int addrlen; addrlen = sizeof(addr); if(event & EV_READ) { printf("[accept_handler]\n"); new_fd = accept(fd, (struct sockaddr*)&addr, &addrlen); new_ev = malloc(sizeof(struct event)); event_set(new_ev, new_fd, EV_READ|EV_PERSIST, read_handler, new_ev); event_add(new_ev, NULL); } } int main (int argc, char **argv) { struct event ev; SOCKET sock; int on; int rc; struct sockaddr_in addr; int server_port = 8888; WORD version = MAKEWORD (1,1); WSADATA wsadata; WSAStartup (version, &wsadata); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket() failed"); WSACleanup(); exit(-1); } rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); if (rc < 0) { perror("setsockopt() failed"); closesocket(sock); WSACleanup(); exit(-1); } rc = ioctlsocket(sock, FIONBIO, &on); if (rc < 0) { perror("ioctl() failed"); closesocket(sock); WSACleanup(); exit(-1); } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(server_port); rc = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); if (rc < 0) { perror("bind() failed"); closesocket(sock); WSACleanup(); exit(-1); } rc = listen(sock, 32); if (rc < 0) { perror("listen() failed"); closesocket(sock); WSACleanup(); exit(-1); } event_init(); event_set(&ev, (int)socket, EV_READ | EV_PERSIST, accept_handler, &ev); event_add(&ev, NULL); event_dispatch(); closesocket(sock); WSACleanup(); return (0); }
这个程序并没有在event_dispatch()处进入监听状态,而是在main函数最后的return (0);处退出,
后来发现bug其实在这里
event_init(); event_set(&ev, (int)socket, EV_READ | EV_PERSIST, accept_handler, &ev); event_add(&ev, NULL);
我在重命名socket对象的名称时忘记改这里。
结果代码把socket这个函数显式地转换为int型,
当时以为强制转换是必须,但事实上不是。
libevent在运行时也没有发现这个问题,于是直接退出event_dispatch()循环。
正确的写法是
event_init(); event_set(&ev, sock, EV_READ | EV_PERSIST, accept_handler, &ev); event_add(&ev, NULL);
结论是:
C代码中任何显式和隐式的类型转换都需要加倍小心,尤其是在重构代码更改变量名的时候。
尽量避免类型转换,不要完全依赖某个特定的C编译器的检查(这里我使用的是VC2008)。