维护两个缓冲区:to容纳从标准输入到服务器的数据,from容纳从服务器到标准输出的数据
toiptr指向从标准输入读入的数据可以存放的下一个字节,tooptr指下一个必须写入到套接口的字节。有(toiptr - tooptr)个字节需写到套接口。friptr表示从套接口读入的数据可以存放的下一个字节,froptr表示下一个必须写到标准输出的字节
将套接口、标准输入、标准输出设置为非阻塞时,如果写或读没有成功,会返回EWOULDBLOCK
服务器端代码:
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define SIN_PORT 9999
#define BUFLEN 256
void str_echo(int fd);
void sig_chld(int signo)
{
pid_t pid;
int stat;
pid = wait(&stat);
printf("child %d terminate\n", pid);
}
int main(int argc, char **argv)
{
int listenfd, connfd;
pid_t child;
struct sockaddr_in servaddr;
char buf[BUFLEN];
time_t ticks;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
printf("socket error :%s\n", strerror(errno));
return -1;
}
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SIN_PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
printf("bind error:%s\n", strerror(errno));
close(listenfd);
return -1;
}
if (listen(listenfd, 3) < 0) {
printf("listen error:%s\n", strerror(errno));
close(listenfd);
return -1;
}
signal(SIGCHLD, sig_chld);
struct sockaddr_in clientaddr;
int len;
for (;;) {
len = sizeof(clientaddr);
connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);
if (connfd < 0) {
if (errno == EINTR) continue;
else {
printf("accept error:%s\n", strerror(errno));
close(listenfd);
return -1;
}
}
if ((child = fork()) == 0) {
close(listenfd);
str_echo(connfd);
return 0;
}
close(connfd);
}
return 0;
}
size_t readn(int fd, void *ptr, size_t n)
{
char *p = ptr;
size_t nleft = n;
size_t nread;
while (nleft > 0) {
if ((nread = read(fd, p, nleft)) < 0) {
if (errno == EINTR) nread = 0;
else return -1;
} else if (nread == 0) break;
nleft -= nread;
p += nread;
}
return n - nleft;
}
size_t readline(int fd, void *ptr, size_t maxsize)
{
char *p = ptr;
size_t rc, n;
char c;
for (n = 1; n < maxsize; n++) {
again:
if ((rc = read(fd, &c, 1)) == 1) {
*p++ = c;
if (c == '\n') break;
} else if (rc == 0) {
if (n == 1) return 0;
else break;
} else {
if (errno == EINTR) goto again;
return -1;
}
}
*p = 0;
return n;
}
size_t writen(int fd, void *ptr, size_t n)
{
char *p = ptr;
size_t nleft = n, nwriten;
while (nleft > 0) {
if ((nwriten = write(fd, p, nleft)) <= 0) {
if (errno == EINTR) nwriten = 0;
else return -1;
}
p += nwriten;
nleft -= nwriten;
}
return n;
}
void str_echo(int fd)
{
char recvline[BUFLEN];
int n;
for (;;) {
if ((n = readline(fd, recvline, BUFLEN)) == 0) return;
printf("received buf=%s", recvline);
writen(fd, recvline, n);
}
}
客户端代码:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#define SERV_PORT 9999
#define MAXLINE 128
#define MAX(a, b) ((a) < (b) ? (b) : (a))
char *gf_time()
{
struct timeval tv;
static char str[30];
char *ptr;
if (gettimeofday(&tv, NULL) < 0) {
fprintf(stderr, "gettimeofday error:%s\n", strerror(errno));
return NULL;
}
ptr = ctime(&tv.tv_sec);
strcpy(str, &ptr[11]);
snprintf(str + 8, sizeof(str) - 8, ".%06ld", tv.tv_usec);
return str;
}
void str_cli(FILE *fp, int sockfd)
{
int maxfd1, val, stdineof;
ssize_t n, nwritten;
fd_set rset, wset;
char to[MAXLINE], from[MAXLINE];
char *toiptr, *tooptr, *friptr, *froptr;
val = fcntl(sockfd, F_GETFL, 0);
if (fcntl(sockfd, F_SETFL, val | O_NONBLOCK) < 0) {
printf("fcntl error:%s\n", strerror(errno));
close(sockfd);
return;
}
val = fcntl(STDIN_FILENO, F_GETFL, 0);
if (fcntl(STDIN_FILENO, F_SETFL, val | O_NONBLOCK) < 0) {
printf("fcntl stdin error:%s\n", strerror(errno));
close(sockfd);
return;
}
val = fcntl(STDOUT_FILENO, F_GETFL, 0);
if (fcntl(STDOUT_FILENO, F_SETFL, val | O_NONBLOCK) < 0) {
printf("fcntl stdout error:%s\n", strerror(errno));
close(sockfd);
return ;
}
toiptr = tooptr = to;
friptr = froptr = from;
stdineof = 0;
maxfd1 = MAX(sockfd, MAX(STDIN_FILENO, STDOUT_FILENO)) + 1;
for (;;) {
FD_ZERO(&rset);
FD_ZERO(&wset);
if (stdineof == 0 && toiptr < &to[MAXLINE]) {
FD_SET(STDIN_FILENO, &rset);
}
if (friptr < &from[MAXLINE]) {
FD_SET(sockfd, &rset);
}
if (tooptr != toiptr) {
FD_SET(sockfd, &wset);
}
if (froptr != friptr) {
FD_SET(STDOUT_FILENO, &wset);
}
int res;
if ((res = select(maxfd1, &rset, &wset, NULL, NULL)) < 0) {
if (errno != EINTR) {
close(sockfd);
return;
}
}
if (FD_ISSET(STDIN_FILENO, &rset)) {
if ((n = read(STDIN_FILENO, toiptr, &to[MAXLINE] - toiptr)) < 0) {
if (errno != EWOULDBLOCK) {
printf("read error from stdin:%s\n", strerror(errno));
close(sockfd);
return;
}
} else if (n == 0) {
fprintf(stderr, "%s:EOF on stdin\n", gf_time());
stdineof = 1;
if (toiptr == tooptr) shutdown(sockfd, SHUT_WR);
} else {
fprintf(stderr, "%s:read %d bytes from stdin\n", gf_time(), n);
toiptr += n;
FD_SET(sockfd, &wset);
}
}
if (FD_ISSET(sockfd, &rset)) {
if ((n = read(sockfd, friptr, &from[MAXLINE] - friptr)) < 0) {
if (errno != EWOULDBLOCK) {
printf("read error on socket:%s\n", strerror(errno));
close(sockfd);
return;
}
} else if (n == 0) {
fprintf(stderr, "%s :EOF on socket\n", gf_time());
if (stdineof) return;
} else {
fprintf(stderr, "%s:read %d bytes from socket\n", gf_time(), n);
friptr += n;
FD_SET(STDOUT_FILENO, &wset);
}
}
if (FD_ISSET(STDOUT_FILENO, &wset) && ((n = friptr - froptr) > 0)) {
if ((nwritten = write(STDOUT_FILENO, froptr, n)) < 0) {
if (errno != EWOULDBLOCK) {
printf("write stdout error:%s\n", strerror(errno));
close(sockfd);
return;
}
} else {
fprintf(stderr, "%s:write %d bytes to stdout\n", gf_time(), nwritten);
froptr += nwritten;
if (froptr == friptr) friptr = froptr = from;
}
}
if (FD_ISSET(sockfd, &wset) && ((n = toiptr - tooptr) > 0)) {
if ((nwritten = write(sockfd, tooptr, n)) < 0) {
if (errno != EWOULDBLOCK) {
fprintf(stderr, "write on socket error:%s\n", strerror(errno));
close(sockfd);
return;
}
} else {
fprintf(stderr, "%s:write %d bytes to socket\n", gf_time(), nwritten);
tooptr += nwritten;
if (tooptr == toiptr) {
toiptr = tooptr = to;
if (stdineof) shutdown(sockfd, SHUT_WR);
}
}
}
}
}
int main(int argc, char **argv)
{
struct sockaddr_in servaddr, cliaddr;
socklen_t len;
int sockfd;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0) {
printf("inet_pton error:%s\n", strerror(errno));
return -1;
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
printf("socket error:%s\n", strerror(errno));
return -1;
}
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
printf("connect error:%s\n", strerror(errno));
close(sockfd);
return -1;
}
str_cli(stdin, sockfd);
return 0;
}