在“The Linux Programming Interface”一书中,有一个功能:阅读如下:
ssize_t readn(int fd, void *buffer, size_t n)
{
ssize_t numRead; /* # of bytes fetched by last read() */
size_t totRead; /* Total # of bytes read so far */
char *buf;
buf = buffer; /* No pointer arithmetic on "void *" */
for (totRead = 0; totRead < n; ) {
numRead = read(fd, buf, n - totRead);
if (numRead == 0) /* EOF */
return totRead; /* May be 0 if this is first read() */
if (numRead == -1) {
if (errno == EINTR)
continue; /* Interrupted --> restart read() */
else
return -1; /* Some other error */
}
totRead += numRead;
buf += numRead;
}
return totRead; /* Must be 'n' bytes if we get here */
}
套接字客户端可能首先发送:0123 告诉套接字服务器消息是 123 字节!我通过以下方式使用 readn :
while(1){
iRead = readn(fd,buffer,4) ;
if(iRead <= 0)
break ;
buffer[4]=0x00;
iMsglen = atoi(buffer) ;
iRead = readn(fd,buffer,iMsglen) ;
if(iRead <= 0)
break ;
//do something for buffer
} //while
如果套接字客户端首先向套接字服务器发送“0123”,但如果以 2 个包交付,即“01”和“23”,则 readn 函数 readn(fd,buffer,4) 将确保将“01”和“ 23" 到 "0123" 并返回 4,然后应用程序知道后面的消息长度为 123 字节!readn 是一个非常有用的功能...
后来我用谷歌搜索了 libevent 2.0 教程,有一个回显服务器,它具有以下功能:
void read_cb(struct bufferevent *bev, void *arg)
{
#define MAX_LINE 256
char line[MAX_LINE+1];
int n;
evutil_socket_t fd = bufferevent_getfd(bev);
while (n = bufferevent_read(bev, line, MAX_LINE), n > 0) {
line[n] = '\0';
printf("fd=%u, read line: %s\n", fd, line);
bufferevent_write(bev, line, n);
}
}
如果发生任何套接字 fd 接收消息事件,则调用函数 read_cb ,而不是像上面的 readn 那样管理每个文件描述符的缓冲区来实现,有没有更简单的方法来做到这一点?我的意思是,函数名称类似于:
bufferevent_readn(bev, line, 4)
如果我的示例的网络包,“01”发送到服务器,在“23”之后,由于 bufferevent_readn 的第 3 个参数是 4,那么“01”接收到它可能会忽略该事件,直到它接收到所有 4 个字节客户端然后拍摄事件,例如在 libevent 的 API 中构建 readn 函数!
任何建议,评论表示赞赏!
编辑 :
在我更详细地研究了 libevent 文档之后,我认为 libevent 真的很棒!
http://www.wangafu.net/~nickm/libevent-book/Ref6_bufferevent.html
有几个函数可以做正确的工作,如以下来源所示:
bufferevent_setwatermark 和 evbuffer_get_length 和 evbuffer_drain 正是我所需要的......感谢您的出色工作!
struct info {
const char *name;
size_t total_drained;
};
void read_callback(struct bufferevent *bev, void *ctx)
{
struct info *inf = ctx;
struct evbuffer *input = bufferevent_get_input(bev);
size_t len = evbuffer_get_length(input);
if (len) {
inf->total_drained += len;
evbuffer_drain(input, len);
printf("Drained %lu bytes from %s\n",
(unsigned long) len, inf->name);
}
}
void event_callback(struct bufferevent *bev, short events, void *ctx)
{
struct info *inf = ctx;
struct evbuffer *input = bufferevent_get_input(bev);
int finished = 0;
if (events & BEV_EVENT_EOF) {
size_t len = evbuffer_get_length(input);
printf("Got a close from %s. We drained %lu bytes from it, "
"and have %lu left.\n", inf->name,
(unsigned long)inf->total_drained, (unsigned long)len);
finished = 1;
}
if (events & BEV_EVENT_ERROR) {
printf("Got an error from %s: %s\n",
inf->name, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
finished = 1;
}
if (finished) {
free(ctx);
bufferevent_free(bev);
}
}
struct bufferevent *setup_bufferevent(void)
{
struct bufferevent *b1 = NULL;
struct info *info1;
info1 = malloc(sizeof(struct info));
info1->name = "buffer 1";
info1->total_drained = 0;
/* ... Here we should set up the bufferevent and make sure it gets
connected... */
/* Trigger the read callback only whenever there is at least 128 bytes
of data in the buffer. */
bufferevent_setwatermark(b1, EV_READ, 128, 0);
bufferevent_setcb(b1, read_callback, NULL, event_callback, info1);
bufferevent_enable(b1, EV_READ); /* Start reading. */
return b1;
}
编辑2:
http://www.wangafu.net/~nickm/libevent-book/Ref7_evbuffer.html
evbuffer_pullup 函数允许您查看缓冲区中的数据!
int parse_socks4(struct evbuffer *buf, ev_uint16_t *port, ev_uint32_t *addr)
{
/* Let's parse the start of a SOCKS4 request! The format is easy:
* 1 byte of version, 1 byte of command, 2 bytes destport, 4 bytes of
* destip. */
unsigned char *mem;
mem = evbuffer_pullup(buf, 8);
if (mem == NULL) {
/* Not enough data in the buffer */
return 0;
} else if (mem[0] != 4 || mem[1] != 1) {
/* Unrecognized protocol or command */
return -1;
} else {
memcpy(port, mem+2, 2);
memcpy(addr, mem+4, 4);
*port = ntohs(*port);
*addr = ntohl(*addr);
/* Actually remove the data from the buffer now that we know we
like it. */
evbuffer_drain(buf, 8);
return 1;
}
}
编辑3:
该网页有我需要的确切样本,是这篇文章的完美样本!!!!
int get_record(struct evbuffer *buf, size_t *size_out, char **record_out)
{
/* Let's assume that we're speaking some protocol where records
contain a 4-byte size field in network order, followed by that
number of bytes. We will return 1 and set the 'out' fields if we
have a whole record, return 0 if the record isn't here yet, and
-1 on error. */
size_t buffer_len = evbuffer_get_length(buf);
ev_uint32_t record_len;
char *record;
if (buffer_len < 4)
return 0; /* The size field hasn't arrived. */
/* We use evbuffer_copyout here so that the size field will stay on
the buffer for now. */
evbuffer_copyout(buf, &record_len, 4);
/* Convert len_buf into host order. */
record_len = ntohl(record_len);
if (buffer_len < record_len + 4)
return 0; /* The record hasn't arrived */
/* Okay, _now_ we can remove the record. */
record = malloc(record_len);
if (record == NULL)
return -1;
evbuffer_drain(buf, 4);
evbuffer_remove(buf, record, record_len);
*record_out = record;
*size_out = record_len;
return 1;
}