流缓冲管理,
ustream_fd
跟uloop_fd
有什么不一样呢?ustream_fd
内部其实就是uloop_fd
,与fopen和open类似,fopen的内部也是open加上流缓冲管理。
struct ustream_fd {
struct ustream stream;
struct uloop_fd fd;
};
ustream相关的函数
void ustream_fd_init(struct ustream_fd *s, int fd)
void ustream_init_defaults(struct ustream *s)
void ustream_free(struct ustream *s)
ustream的应用在uhttpd的socket监听里面有使用到
bool uh_accept_client(int fd, bool tls)
{
static struct client *next_client;
struct client *cl;
unsigned int sl;
int sfd;
static int client_id = 0;
struct sockaddr_in6 addr;
if (!next_client)
next_client = calloc(1, sizeof(*next_client));
cl = next_client;
sl = sizeof(addr);
sfd = accept(fd, (struct sockaddr *) &addr, &sl);
if (sfd < 0)
return false;
set_addr(&cl->peer_addr, &addr);
sl = sizeof(addr);
getsockname(sfd, (struct sockaddr *) &addr, &sl);
set_addr(&cl->srv_addr, &addr);
cl->us = &cl->sfd.stream;
if (tls) {
uh_tls_client_attach(cl);
} else {
cl->us->notify_read = client_ustream_read_cb;
cl->us->notify_write = client_ustream_write_cb;
cl->us->notify_state = client_notify_state;
}
cl->us->string_data = true;
ustream_fd_init(&cl->sfd, sfd);
uh_poll_connection(cl);
list_add_tail(&cl->list, &clients);
next_client = NULL;
n_clients++;
cl->id = client_id++;
cl->tls = tls;
return true;
}
ustream在socket上面的使用例子:
//客户端//
#include "std.h"
/* 在TCP连接持续多少秒没有报文往来之后则开始发送探测报文 */
int tcp_keepalive_time = 5;
/* TCP keepalive探测包的发送间隔 */
int tcp_keepalive_intvl = 5;
/* 在tcp_keepalive_time之后没有收到对方确认,继续发送保活探测包的次数 */
int tcp_keepalive_probes = 3;
int fd = -1;
struct ustream_fd ufd; // uloop机制监听的结构体
static void notify_read_cb(struct ustream *s, int bytes)
{
char *data = NULL;
int len;
data = ustream_get_read_buf(s, &len);
printf("eason recv data:%d available len:%d", bytes, len);
if(!data){
printf("eason recv packet len is too short, wait more data");
return;
}
ustream_consume(s, len);
}
/**************************************************************************************
* FunctionName : notify_state_cb()
* Description : uloop+ustream框架通知连接状态发生变化的回调,如果检测到对端断开则本地断开连接
* EntryParameter : 指向当前活跃的ustream的指针
* ReturnValue : NULL
**************************************************************************************/
static void notify_state_cb(struct ustream *s)
{
if (!s->eof) return;
printf("eason misock client got eof!, pending:%d", s->w.data_bytes);
close(fd);
}
/**************************************************************************************
* FunctionName : notify_write_cb()
* Description : uloop+ustream框架通知缓冲数据已经被写入到stream中
* EntryParameter : 指向当前活跃的ustream的指针
* ReturnValue : NULL
**************************************************************************************/
static void notify_write_cb(struct ustream *s, int bytes)
{
printf("eason misock client wrote:%d bytes, pending:%d", bytes, s->w.data_bytes);
}
static void setup_misock_client_ustream()
{
ufd.stream.string_data = false;
ufd.stream.notify_read = notify_read_cb;
ufd.stream.notify_state = notify_state_cb;
ufd.stream.notify_write = notify_write_cb;
ustream_fd_init(&ufd, fd);
}
int main(void)
{
struct sockaddr_in server;
int optval;
struct timeval tval;
fd_set r_set, w_set;
int rc = 0;
// 1.创建
if (0 > (fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))
err_exit("socket");
// 2.模式
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); //设置非阻塞模式
// 3.绑定
// 4.连接
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(8888);
server.sin_addr.s_addr = inet_addr("192.168.201.130");
if (0 > connect(fd, (struct sockaddr *)&server, sizeof(server)))
err_exit("connect");
printf("connect success!\n");
/* 4.KEEPALIVE相关配置 */
/*使能keepalive*/
optval =1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
/* 配置tcp_keepalive_time */
optval = tcp_keepalive_time;
setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof(optval));
/* 配置tcp_keepalive_intvl */
optval = tcp_keepalive_intvl;
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof(optval));
/* 配置tcp_keepalive_probes */
optval = tcp_keepalive_probes;
setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &optval, sizeof(optval));
// 4.确定连接已经成功
FD_ZERO(&r_set);
FD_SET(fd, &r_set);
FD_ZERO(&w_set);
FD_SET(fd, &w_set);
tval.tv_sec = 1;
tval.tv_usec = 0;
rc = select(fd+1, &r_set, &w_set, NULL, &tval);
if(rc <= 0){
printf("eason fun=%s,line=%d", __FUNCTION__,__LINE__);
goto RETURN_1;
}
printf("eason connect fd=%d,%d,%d,%d",fd,rc,FD_ISSET(fd, &w_set),FD_ISSET(fd, &r_set));
if (rc == 1 && FD_ISSET(fd, &w_set) && !FD_ISSET(fd, &r_set)) {
printf("eason connected\n");
setup_misock_client_ustream();
return NULL; // connected
}
printf("eason connect failed fun=%s,line=%d", __FUNCTION__,__LINE__);
RETURN_1:
// 10.关闭
close(fd);
return NULL;
#if 0
char send_buf[BUFSIZ];
char recv_buf[BUFSIZ];
int maxfd= -1;
fd_set readfds;
int ret=-1;
int i;
while(1) {
FD_ZERO(&readfds);
FD_SET(0, &readfds);
maxfd = maxfd > 0 ? maxfd : 0;
FD_SET(connfd, &readfds);
maxfd = maxfd > connfd ? maxfd : connfd;
if (0 > select(maxfd + 1, &readfds, NULL, NULL, NULL))
err_exit("select");
for (i=0; i<=maxfd; i++)
{
if (FD_ISSET(i, &readfds))
{
if (0 == i)
{
fgets(send_buf, sizeof(send_buf), stdin);
if (0 > send(connfd, send_buf, strlen(send_buf) + 1, 0))
err_exit("send");
}
else if (connfd == i)
{
if (0 > (ret = recv(connfd, recv_buf, BUFSIZ, 0)))
err_exit("recv");
else if (0 == ret)
{
printf("server quit!\n");
exit(0);
}
recv_buf[ret] = '\0';
printf("server: %s\n", recv_buf);
}
}
}
}
close(connfd);
exit(0);
#endif
}
另外在libubox里面也有自带一个例子:
位于/example/ustream-example.c
#include
#include
#include
#include
#include
#include
#include
#include "ustream.h"
#include "uloop.h"
#include "usock.h"
static struct uloop_fd server;
static const char *port = "10000";
struct client *next_client = NULL;
struct client {
struct sockaddr_in sin;
struct ustream_fd s;
int ctr;
};
static void client_read_cb(struct ustream *s, int bytes)
{
struct client *cl = container_of(s, struct client, s.stream);
struct ustream_buf *buf = s->r.head;
char *newline, *str;
do {
str = ustream_get_read_buf(s, NULL);
if (!str)
break;
newline = strchr(buf->data, '\n');
if (!newline)
break;
*newline = 0;
ustream_printf(s, "%s\n", str);
ustream_consume(s, newline + 1 - str);
cl->ctr += newline + 1 - str;
} while(1);
if (s->w.data_bytes > 256 && !ustream_read_blocked(s)) {
fprintf(stderr, "Block read, bytes: %d\n", s->w.data_bytes);
ustream_set_read_blocked(s, true);
}
}
static void client_close(struct ustream *s)
{
struct client *cl = container_of(s, struct client, s.stream);
fprintf(stderr, "Connection closed\n");
ustream_free(s);
close(cl->s.fd.fd);
free(cl);
}
static void client_notify_write(struct ustream *s, int bytes)
{
fprintf(stderr, "Wrote %d bytes, pending: %d\n", bytes, s->w.data_bytes);
if (s->w.data_bytes < 128 && ustream_read_blocked(s)) {
fprintf(stderr, "Unblock read\n");
ustream_set_read_blocked(s, false);
}
}
static void client_notify_state(struct ustream *s)
{
struct client *cl = container_of(s, struct client, s.stream);
if (!s->eof)
return;
fprintf(stderr, "eof!, pending: %d, total: %d\n", s->w.data_bytes, cl->ctr);
if (!s->w.data_bytes)
return client_close(s);
}
static void server_cb(struct uloop_fd *fd, unsigned int events)
{
struct client *cl;
unsigned int sl = sizeof(struct sockaddr_in);
int sfd;
if (!next_client)
next_client = calloc(1, sizeof(*next_client));
cl = next_client;
sfd = accept(server.fd, (struct sockaddr *) &cl->sin, &sl);
if (sfd < 0) {
fprintf(stderr, "Accept failed\n");
return;
}
cl->s.stream.string_data = true;
cl->s.stream.notify_read = client_read_cb;
cl->s.stream.notify_state = client_notify_state;
cl->s.stream.notify_write = client_notify_write;
ustream_fd_init(&cl->s, sfd);
next_client = NULL;
fprintf(stderr, "New connection\n");
}
static int run_server(void)
{
server.cb = server_cb;
server.fd = usock(USOCK_TCP | USOCK_SERVER | USOCK_IPV4ONLY | USOCK_NUMERIC, "127.0.0.1", port);
if (server.fd < 0) {
perror("usock");
return 1;
}
uloop_init();
uloop_fd_add(&server, ULOOP_READ);
uloop_run();
return 0;
}
static int usage(const char *name)
{
fprintf(stderr, "Usage: %s -p \n", name);
return 1;
}
int main(int argc, char **argv)
{
int ch;
while ((ch = getopt(argc, argv, "p:")) != -1) {
switch(ch) {
case 'p':
port = optarg;
break;
default:
return usage(argv[0]);
}
}
return run_server();
}
参考文件
https://blog.csdn.net/TSZ0000/article/details/103558627
https://segmentfault.com/a/1190000012104130