STREAMS是一个framework,使用TPI(Transport Provider Interface,基于STREAMS的在传输层的接口)创建应用。
STREAMS在进程和driver(也可以是一个software driver)之间提供一个full-duplex的连接
当对STREAMS descriptor进行函数调用时(read, putmsg, ioctl),相关的实现由stream head提供。
进程可在stream head和driver之间动态插入和移除processing modules。一个module提供了一个filtering
multiplexor是一个预置的module,它可以从多个源接收数据
- 当一个socket被创建,sockmod被sockets library插入到stream中,sockets library和sockmod STREAMS module组合起来提供sockets API给进程
- 不同的服务提供不同的消息格式
TPI定义了transport-layer provider提供的接口(TCP,UDP)
NPI(Network Provider Interface)定义了network-layer provider提供的接口(IP)
DLPI(Data Link Provider Interface)
stream中的所有组件:stream head,processing modules,driver都至少包含两个队列:write queue和read queue
Message Types
STREAMS消息分为high priority, priority band, normal。priority band的范围从0-255,normal消息为band 0,优先级用在消息队列和flow control中,如对high-priority消息不受flow control影响。
网络协议一般用band 1当作紧急消息,band 0用作一般消息
TCP的out-of-band用作band 0。
有3种类型的消息要关心一下:
消息分为控制和数据。
#include <stropts.h>
int getmsg(int fd, struct strbuf *ctlptr, struct strbuf *dataptr, int *flagsp) ;
返回0表示所有数据都返回了,如果control buffer太小,返回MORECTL。如果data buffer太小,返回MOREDATA。两者都太小,返回两者的OR
int putmsg(int fd, const struct strbuf *ctlptr, const struct strbuf *dataptr, int flags) ;
错误返回-1,成功返回0
可同时操作控制消息和数据。
struct strbuf
{
int maxlen; /* maximum size of buf */
int len; /* actual amount of data in buf */
char *buf; /* data */
};
如果不发送控制消息,ctlptr=0,或 ctlptr->len = -1
没有控制消息,类型为M_DATA,有控制消息,类型为M_PROTO(flags为0)或M_PCPROTO(flags 为RS_HIPRI)
int putpmsg(int fd, const struct strbuf *ctlptr, const struct strbuf *dataptr, int band, int flags) ;
band在0-255这间,如果flags为MSG_BAND,消息的优先级为指定的band(为0相当于调用putmsg)。flags为MSG_HIPRI,band必须为0,高优先级消息产生。
int getpmsg(int fd, struct strbuf *ctlptr, struct strbuf *dataptr, int *bandp, int *flagsp) ;
flagsp可为MSG_HIPRI(读高优先级消息),MSG_BAND(消息的优先级最少等于bandp),MSG_ANY(读所有消息)
int ioctl(int fd, int request, ... /* void *arg */ ) ;
成功返回0,错误返回-1
Transport Provider Interface (TPI)
提供传输层的接口,在STREAMS环境中sockets和XTI都使用这一接口。它由sockets library和sockmod组成,也由XTI library和timod组成。用TCP和UDP传送TPI消息。
TPI是message-based接口。它定义了进程和传输层传递消息的格式
void tpi_bind(int fd, const void *addr, size_t addrlen)
{
struct {
struct T_bind_req msg_hdr;
char addr[128];
} bind_req;
struct {
struct T_bind_ack msg_hdr;
char addr[128];
} bind_ack;
struct strbuf ctlbuf;
struct T_error_ack *error_ack;
int flags;
bind_req.msg_hdr.PRIM_type = T_BIND_REQ;
bind_req.msg_hdr.ADDR_length = addrlen;
bind_req.msg_hdr.ADDR_offset = sizeof(struct T_bind_req);
bind_req.msg_hdr.CONIND_number = 0;
memcpy(bind_req.addr, addr, addrlen); /* sockaddr_in{} */
ctlbuf.len = sizeof(struct T_bind_req) + addrlen;
ctlbuf.buf = (char *) &bind_req;
Putmsg(fd, &ctlbuf, NULL, 0);
ctlbuf.maxlen = sizeof(bind_ack);
ctlbuf.len = 0;
ctlbuf.buf = (char *) &bind_ack;
flags = RS_HIPRI;
Getmsg(fd, &ctlbuf, NULL, &flags);
if (ctlbuf.len < (int) sizeof(long))
err_quit("bad length from getmsg");
switch (bind_ack.msg_hdr.PRIM_type) {
case T_BIND_ACK:
return;
case T_ERROR_ACK:
if (ctlbuf.len < (int) sizeof(struct T_error_ack))
err_quit("bad length for T_ERROR_ACK");
error_ack = (struct T_error_ack *) &bind_ack.msg_hdr;
err_quit("T_ERROR_ACK from bind (%d, %d)",
error_ack->TLI_error, error_ack->UNIX_error);
default:
err_quit("unexpected message type: %d", bind_ack.msg_hdr.PRIM_type);
}
}
void tpi_connect(int fd, const void *addr, size_t addrlen)
{
struct {
struct T_conn_req msg_hdr;
char addr[128];
} conn_req;
struct {
struct T_conn_con msg_hdr;
char addr[128];
} conn_con;
struct strbuf ctlbuf;
union T_primitives rcvbuf;
struct T_error_ack *error_ack;
struct T_discon_ind *discon_ind;
int flags;
conn_req.msg_hdr.PRIM_type = T_CONN_REQ;
conn_req.msg_hdr.DEST_length = addrlen;
conn_req.msg_hdr.DEST_offset = sizeof(struct T_conn_req);
conn_req.msg_hdr.OPT_length = 0;
conn_req.msg_hdr.OPT_offset = 0;
memcpy(conn_req.addr, addr, addrlen); /* sockaddr_in{} */
ctlbuf.len = sizeof(struct T_conn_req) + addrlen;
ctlbuf.buf = (char *) &conn_req;
Putmsg(fd, &ctlbuf, NULL, 0);
ctlbuf.maxlen = sizeof(union T_primitives);
ctlbuf.len = 0;
ctlbuf.buf = (char *) &rcvbuf;
flags = RS_HIPRI;
Getmsg(fd, &ctlbuf, NULL, &flags);
if (ctlbuf.len < (int) sizeof(long))
err_quit("tpi_connect: bad length from getmsg");
switch (rcvbuf.type) {
case T_OK_ACK:
break;
case T_ERROR_ACK:
if (ctlbuf.len < (int) sizeof(struct T_error_ack))
err_quit("tpi_connect: bad length for T_ERROR_ACK");
error_ack = (struct T_error_ack *) &rcvbuf;
err_quit("tpi_connect: T_ERROR_ACK from conn (%d, %d)",
error_ack->TLI_error, error_ack->UNIX_error);
default:
err_quit("tpi_connect: unexpected message type: %d", rcvbuf.type);
}
ctlbuf.maxlen = sizeof(conn_con);
ctlbuf.len = 0;
ctlbuf.buf = (char *) &conn_con;
flags = 0;
Getmsg(fd, &ctlbuf, NULL, &flags);
if (ctlbuf.len < (int) sizeof(long))
err_quit("tpi_connect2: bad length from getmsg");
switch (conn_con.msg_hdr.PRIM_type) {
case T_CONN_CON:
break;
case T_DISCON_IND:
if (ctlbuf.len < (int) sizeof(struct T_discon_ind))
err_quit("tpi_connect2: bad length for T_DISCON_IND");
discon_ind = (struct T_discon_ind *) &conn_con.msg_hdr;
err_quit("tpi_connect2: T_DISCON_IND from conn (%d)",
discon_ind->DISCON_reason);
default:
err_quit("tpi_connect2: unexpected message type: %d",
conn_con.msg_hdr.PRIM_type);
}
}
ssize_t tpi_read(int fd, void *buf, size_t len)
{
struct strbuf ctlbuf;
struct strbuf datbuf;
union T_primitives rcvbuf;
int flags;
976
Amber CHM Converter Trial version, http://www.processtext.com/abcchm.html
ctlbuf.maxlen = sizeof(union T_primitives);
ctlbuf.buf = (char *) &rcvbuf;
datbuf.maxlen = len;
datbuf.buf = buf;
datbuf.len = 0;
flags = 0;
Getmsg(fd, &ctlbuf, &datbuf, &flags);
if (ctlbuf.len >= (int) sizeof(long)) {
if (rcvbuf.type == T_DATA_IND)
return (datbuf.len);
else if (rcvbuf.type == T_ORDREL_IND)
return (0);
else
err_quit("tpi_read: unexpected type %d", rcvbuf.type);
} else if (ctlbuf.len == -1)
return (datbuf.len);
else
err_quit("tpi_read: bad length from getmsg");
}
void tpi_close(int fd)
{
struct T_ordrel_req ordrel_req;
struct strbuf ctlbuf;
ordrel_req.PRIM_type = T_ORDREL_REQ;
ctlbuf.len = sizeof(struct T_ordrel_req);
ctlbuf.buf = (char *) &ordrel_req;
Putmsg(fd, &ctlbuf, NULL, 0);
Close(fd);
}