第十四章 高级I/O函数:
#include <sys/socket.h>
ssize_t recv(int sockfd, void * buff, size_t nbytes, int flags);
success return recv-byte-count, error return -1
ssize_t send(int sockfd, const void * buff, size_t nbytes, int flags);
success return send-byte-count, error return -1
flags:
MSG_DONTROUTE, MSG_DONTWAIT, MSG_OOB, MSG_PEEK, MSG_WAITALL
/* if system support MSG_WAITALL */
#define readn(fd, ptr, n) recv(fd, ptr, n, MSG_WAITALL)
#include <sys/uio.h>
ssize_t readv(int fileds, const struct iovec * iov, int iovcnt);
success return total-read-byte-count, error return -1
ssize_t writev(int fileds, const struct iovec * iov, int iovcnt);
success return total-write-byte-count, error return -1
struct iovec {
void * iov_base; /* starting address of buffer */
size_t iov_len; /* size of buffer */
};
#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr * msg, int flags);
success return recv-byte-count, error return -1
ssize_t sendmsg(int sockfd, struct msghdr * msg, int flags);
success return send-byte-count, error return -1
struct msghdr {
void * msg_name; /* protocol address */
socklen_t msg_namelen; /* size of protocol address (val-ret)*/
struct iovec * msg_iov; /* scatter/gather array */
int msg_iovlen; /* element-count in msg_iov */
void * msg_control; /* ancillary data (cmsghdr struct) */
socklen_t msg_controllen; /* length of ancillary data */
int msg_flags; /* flags returned by recvmsg() */
...
};
flags:
MSG_DONTROUTE, MSG_DONTWAIT, MSG_OOB, MSG_PEEK, MSG_WAITALL
msg_flags:
MSG_DONTROUTE, MSG_DONTWAIT, MSG_OOB, MSG_PEEK, MSG_WAITALL
MSG_BCAST, MSG_MCAST, MSG_TRUNC, MSG_CTRUNC, MSG_EOR, MSG_NOTIFICATION
struct cmsghdr {
socklen_t cmsg_len; /* length in bytes, including this structure */
int cmsg_level; /* originating protocol */
int cmsg_type; /* protocol-specific type */
/* followed by unsigned char cmsg_data[] */
};
#include <sys/socket.h>
#include <sys/param.h> /* for ALIGN macro on many implementations */
struct cmsghdr * CMSG_FIRSTHDR(struct msghdr * mhdrptr);
struct cmsghdr * CMSG_NXTHDR(struct msghdr * mhdrptr,
struct cmsghdr * cmsgptr);
unsigned char * CMSG_DATA(struct cmsghdr * cmsgptr);
unsigned int CMSG_LENGTH(unsigned int length);
unsigned int CMSG_SPACE(unsigned int length);
usage:
struct msghdr msg;
struct cmsghdr * cmsgptr;
/* fill in msg structure */
/* call recvmsg() */
for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
if (cmsgptr->cmsg_level == ... &&
cmsgptr->cmsg_type == ...) {
u_char * ptr = CMSG_DATA(cmsgptr);
/* process data */
}
}
/dev/poll - interface:
#include <sys/devpoll.h>
struct dvpoll {
struct pollfd * dp_fds;
int dp_nfds;
int dp_timeout;
};
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
int kqueue(void);
int kevent(int kq, const struct kevent * changelist, int nchanges,
struct kevent * eventlist, int nevents,
const struct timespec * timeout);
void EV_SET(struct kevent * kev, uintptr_t ident, short filter,
u_short flags, u_int fflags, intptr_t data, void * udata);
struct kevent {
uintptr_t ident;
short filter;
u_short flags;
u_int fflags;
intptr_t data;
void * udata;
};
flags:
EV_ADD, EV_CLEAR, EV_DELETE, (value)
EV_DISABLE, EV_ENABLE, EV_ONESHOT, (value)
EV_EOF, EV_ERROR (result)
filter:
EVFILT_AIO, EVFILT_PROC, EVFILT_READ, EVFILT_SIGNAL,
EVFILT_TIMER, EVFILT_VNODE, EVFILT_WRITE
示例:
#include <netinet/in.h>
#include "err_exit.h"
#include "my_signal.h"
#define MAXLINE 4096
void
sig_alrm(int signo)
{
return; /* just interrupt the recvfrom() */
}
void
dg_cli(FILE * fp, int sockfd, const struct sockaddr * pservaddr,
socklen_t servlen)
{
int n;
char sendline[MAXLINE];
char recvline[MAXLINE + 1];
if (my_signal(SIGALRM, sig_alrm) == SIG_ERR) {
err_exit("my_signal error");
}
while (fgets(sendline, MAXLINE, fp) != NULL) {
n = strlen(sendline);
if (sendto(sockfd, sendline, n, 0, pservaddr, servlen) != n) {
err_exit("sendto error");
}
alarm(5);
if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
if (errno == EINTR) {
fprintf(stderr, "socket timeout\n");
}
else {
err_exit("recvfrom error");
}
}
else {
alarm(0);
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF) {
err_exit("fputs error");
}
}
}
if (ferror(fp)) {
err_exit("fgets error");
}
}
#include <netinet/in.h>
#include "err_exit.h"
#include "my_signal.h"
void
connect_alarm(int signo)
{
return; /* just interrupt the connect() */
}
int
connect_timeo(int sockfd, const struct sockaddr * saptr,
socklen_t salen, int nsec)
{
Sigfunc * sigfunc;
int n;
if ((sigfunc = my_signal(SIGALRM, connect_alarm)) == SIG_ERR) {
err_exit("my_signal error");
}
if (alarm(nsec) != 0) {
printf("connect_timeo: alarm was already set\n");
}
if ((n = connect(sockfd, saptr, salen)) < 0) {
if (errno == EINTR) {
errno = ETIMEDOUT;
}
if (close(sockfd) == -1) {
err_exit("close error");
}
}
alarm(0); /* turn off the alarm */
/* restore previous signal handler */
if (my_signal(SIGALRM, sigfunc) == SIG_ERR) {
err_exit("my_signal error");
}
return(n);
}
#include <stdio.h>
#include <sys/select.h>
int
readable_timeo(int fd, int sec)
{
fd_set rset;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(fd, &rset);
tv.tv_sec = sec;
tv.tv_usec = 0;
return(select(fd + 1, &rset, NULL, NULL, &tv));
}
#include <stdio.h>
#include <sys/select.h>
int
writeable_timeo(int fd, int sec)
{
fd_set wset;
struct timeval tv;
FD_ZERO(&wset);
FD_SET(fd, &wset);
tv.tv_sec = sec;
tv.tv_usec = 0;
return(select(fd + 1, NULL, &wset, NULL, &tv));
}
#include <netinet/in.h>
#include "err_exit.h"
#include "readable_timeo.h"
#define MAXLINE 4096
void
dg_cli(FILE * fp, int sockfd, const struct sockaddr * pservaddr,
socklen_t servlen)
{
int n;
char sendline[MAXLINE];
char recvline[MAXLINE + 1];
while (fgets(sendline, MAXLINE, fp) != NULL) {
n = strlen(sendline);
if (sendto(sockfd, sendline, n, 0, pservaddr, servlen) != n) {
err_exit("sendto error");
}
if ((n = readable_timeo(sockfd, 5)) < 0) {
err_exit("readable_timeo error");
}
else if (n == 0) {
fprintf(stderr, "socket timeout\n");
}
else {
if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
err_exit("recvfrom error");
}
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF) {
err_exit("fputs error");
}
}
}
if (ferror(fp)) {
err_exit("fgets error");
}
}
#include <netinet/in.h>
#include "err_exit.h"
#define MAXLINE 4096
void
dg_cli(FILE * fp, int sockfd, const struct sockaddr * pservaddr,
socklen_t servlen)
{
int n;
char sendline[MAXLINE];
char recvline[MAXLINE + 1];
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
err_exit("setsockopt error");
}
while (fgets(sendline, MAXLINE, fp) != NULL) {
n = strlen(sendline);
if (sendto(sockfd, sendline, n, 0, pservaddr, servlen) != n) {
err_exit("sendto error");
}
if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
if (errno == EWOULDBLOCK) {
fprintf(stderr, "socket timeout\n");
}
else {
err_exit("recvfrom error");
}
}
else {
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF) {
err_exit("fputs error");
}
}
}
if (ferror(fp)) {
err_exit("fgets error");
}
}
#include "err_exit.h"
#define MAXLINE 4096
/*
* we'd better not use standant IO functions with socket
*/
void
str_echo(int sockfd)
{
char line[MAXLINE];
FILE * fpin;
FILE * fpout;
if ((fpin = fdopen(sockfd, "r")) == NULL) {
err_exit("fdopen to read error");
}
if ((fpout = fdopen(sockfd, "w")) == NULL) {
err_exit("fdopen to write error");
}
while (fgets(line, MAXLINE, fpin) != NULL) {
if (fputs(line, fpout) == EOF) {
err_exit("fputs error");
}
fflush(fpout); /* default full buffered */
}
if (ferror(fpin)) {
err_exit("fgets error");
}
}
#include "err_exit.h"
#define MAXLINE 4096
/*
* we'd better not use standant IO functions with socket
*/
void
str_echo(int sockfd)
{
char line[MAXLINE];
FILE * fpin;
FILE * fpout;
if ((fpin = fdopen(sockfd, "r")) == NULL) {
err_exit("fdopen to read error");
}
if ((fpout = fdopen(sockfd, "w")) == NULL) {
err_exit("fdopen to write error");
}
if (setvbuf(fpin, NULL, _IOLBF, 0) != 0) { /* line buffered */
err_exit("setvbuf fpin error");
}
if (setvbuf(fpout, NULL, _IOLBF, 0) != 0) { /* line buffered */
err_exit("setvbuf fpout error");
}
while (fgets(line, MAXLINE, fpin) != NULL) {
if (fputs(line, fpout) == EOF) {
err_exit("fputs error");
}
}
if (ferror(fpin)) {
err_exit("fgets error");
}
}
#include <sys/devpoll.h> /* no such file */
#include "writen.h"
#include "err_exit.h"
void
str_cli(FILE * fp, int sockfd)
{
int stdineof;
char buf[MAXLINE];
int n;
int wfd;
int i;
int result;
struct pollfd pofd[2];
struct dvpoll dopoll;
if ((wfd = open("/dev/poll", O_RDWR, 0)) == -1) {
err_exit("open error");
}
pofd[0].fd = fileno(fp);
pofd[0].events = POLLIN;
pofd[0].revents = 0;
pofd[1].fd = sockfd;
pofd[1].events = POLLIN;
pofd[1].revents = 0;
if (write(wfd, pofd, sizeof(pofd) != sizeof(pofd)) {
err_exit("write error");
}
stdineof = 0;
for ( ; ; ) {
/* block until /dev/poll says something is ready */
dopoll.dp_timeout = -1; /* blocking */
dopoll.dp_nfds = 2;
dopoll.dp_fds = pofd;
if ((result = ioctl(wfd, DP_POLL, &dopoll)) == -1) {
err_exit("ioctl error");
}
/* loop through ready file descriptors */
for (i = 0; i < result; ++i) {
if (dopoll.dp_fds[i].fd == sockfd) {
/* socket is readable */
if ((n = read(sockfd, buf, MAXLINE)) < 0) {
err_exit("read error");
}
else if (n == 0) {
if (stdineof == 1) {
return; /* normal termination */
}
else {
err_exit("str_cli: server terminated prematurely");
}
}
if (write(fileno(stdout), buf, n) != n) {
err_exit("write error");
}
}
else {
/* input is readable */
if ((n = read(fileno(fp), buf, MAXLINE)) < 0) {
err_exit("read error");
}
else if (n == 0) {
stdineof = 1;
if (shutdown(sockfd, SHUT_WR) == -1) { /* send FIN */
err_exit("shutdown error");
}
continue;
}
if (writen(sockfd, buf, n) != n) {
err_exit("writen error");
}
}
}
}
}
#include <sys/types.h>
#include <sys/event.h> /* no such file */
#include <sys/time.h>
#include "writen.h"
#include "err_exit.h"
#define MAXLINE 4096
void
str_cli(FILE * fp, int sockfd)
{
int kq;
int i;
int n;
int nev;
int stdineof = 0;
int isfile;
char buf[MAXLINE];
struct kevent kev[2];
struct timespec ts;
struct stat st;
isfile = ((fstat(fileno(fp), &st) == 0) &&
(st.st_mode & S_IFMT) == S_IFREG);
EV_SET(&kev[0], fileno(fp), EVFILT_READ, EV_ADD, 0, 0, NULL);
EV_SET(&kev[1], sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
kq = kqueue();
ts.tv_sec = 0;
ts.tv_nsec = 0;
kevent(kq, kev, 2, NULL, 0, &ts); /* non-blocking */
for ( ; ; ) {
nev = kevent(kq, NULL, 0, kev, 2, NULL); /* blocking */
for (i = 0; i < nev; ++i) {
if (kev[i].ident == sockfd) { /* socket is readable */
if ((n = read(sockfd, buf, MAXLINE)) < 0) {
err_exit("read error");
}
else if (n == 0) {
if (stdineof == 1) {
return; /* normal termination */
}
else {
err_quit("str_cli: server terminated prematurely");
}
}
if (write(fileno(stdout), buf, n) != n) {
err_exit("write error");
}
}
else if (kev[i].ident == fileno(fp)) { /* input is readable */
if ((n = read(fileno(fp), buf, MAXLINE)) < 0) {
err_exit("read error");
}
else if (n > 0) {
if (writen(sockfd, buf, n) != n) {
err_exit("writen error");
}
}
if (n == 0 || (isfile && n == kev[i].data)) {
stdineof = 1;
if (shutdown(sockfd, SHUT_WR) == -1) { /* send FIN */
err_exit("shutdown error");
}
kev[i].flags = EV_DELETE;
kevent(kq, &kev[i], 1, NULL, 0, &ts); /* remove kevent */
}
}
}
}
}