通过fcntl设置套接字,标准输出,标准输入为非阻塞
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/time.h>
#define BUFSIZE 4096
int max(int a,int b) {
return a > b ? a : b;
}
char* gf_time(void) {
struct timeval tv;
static char str[30];
char* ptr;
if (gettimeofday(&tv,NULL) < 0) {
printf("gettimeofday error: %s\n",strerror(errno));
exit(1);
}
ptr = ctime(&tv.tv_sec);
strcpy(str,&ptr[11]);
snprintf(str + 8,sizeof(str) - 8,".%06ld",tv.tv_usec);
return str;
}
void client_noblock_echo(int fd) {
int val;
val = fcntl(fd,F_GETFL,0);
if (fcntl(fd,F_SETFL,val | O_NONBLOCK) < 0) {
printf("fcntl error: %s\n",strerror(errno));
exit(1);
}
val = fcntl(STDIN_FILENO,F_GETFL,0);
if (fcntl(STDIN_FILENO,F_SETFL,val | O_NONBLOCK) < 0) {
printf("fcntl error: %s\n",strerror(errno));
exit(1);
}
val = fcntl(STDOUT_FILENO,F_GETFL,0);
if (fcntl(STDOUT_FILENO,F_SETFL,val | O_NONBLOCK) < 0) {
printf("fcntl error: %s\n",strerror(errno));
exit(1);
}
char to[BUFSIZE],from[BUFSIZE];
char *tws,*twr; // 发送缓冲区准备发送的(wait send) 准备接收的数据(wait receive from stdin)
char *fwo,*fwr; //接收缓冲区准备输出的(wait output to stdout) 准备接收的数据(receive from server)
fd_set rs,ws;
tws = twr = to;
fwo = fwr = from;
int ioeof = 0;
int maxfdp1 = max(fd,max(STDOUT_FILENO,STDIN_FILENO)) + 1;
int n,nw;
for (; ;) {
FD_ZERO(&rs);
FD_ZERO(&ws);
if (ioeof == 0 && twr < &to[BUFSIZE]) {
FD_SET(STDIN_FILENO,&rs);
}
if (fwo != fwr) {
FD_SET(STDOUT_FILENO,&ws);
}
if (tws != twr) {
FD_SET(fd,&ws);
}
if (fwr < &from[BUFSIZE]) {
FD_SET(fd,&rs);
}
if (select(maxfdp1,&rs,&ws,NULL,NULL) < 0) {
printf("select error: %s\n",strerror(errno));
exit(1);
}
if (FD_ISSET(STDIN_FILENO,&rs)) {
if ((n = read(STDIN_FILENO,twr,&to[BUFSIZE] - twr)) < 0) {
if (errno != EWOULDBLOCK) {
printf("read error on stdin\n");
}
}else if (n == 0) {
fprintf(stderr,"%s: EOF on stdin\n",gf_time());
ioeof = 1;
if (tws == twr) {
if (shutdown(fd,SHUT_WR) < 0) {
printf("shutdown error: %s\n",strerror(errno));
exit(1);
}
}
}else {
fprintf(stderr,"%s: read %d bytes from stdin\n",gf_time(),n);
twr += n;
FD_SET(fd,&ws);
}
}
if (FD_ISSET(fd,&rs)) {
if ((n = read(fd,from,&from[BUFSIZE] - fwr)) < 0) {
if (errno != EWOULDBLOCK){
printf("read error on socket\n");
exit(1);
}
}else if (n == 0) {
fprintf(stderr,"%s: EOF on socket\n",gf_time());
if (ioeof) {
return;
}else {
printf("client_noblock_echo: server terminated prematurely\n");
exit(1);
}
}else {
fprintf(stderr,"%s: read %d bytes from socket\n",gf_time(),n);
fwr += n;
FD_SET(STDOUT_FILENO,&ws);
}
}
if (FD_ISSET(STDOUT_FILENO,&ws) && ((n = fwr - fwo) > 0)) {
if ((nw = write(STDOUT_FILENO,fwo,n)) < 0) {
if (errno != EWOULDBLOCK) {
printf("write error to stdout\n");
exit(1);
}
}else {
fprintf(stderr,"%s: wrote %d bytes to stdout\n",gf_time(),nw);
fwo += nw;
if (fwo == fwr) {
fwr = fwo = from;
}
}
}
if (FD_ISSET(fd,&ws) && ((n = twr - tws) > 0)) {
if ((nw = write(fd,tws,n)) < 0) {
if (errno != EWOULDBLOCK) {
printf("write to socket error\n");
exit(1);
}
}else {
fprintf(stderr,"%s: write %d bytes to socket\n",gf_time(),nw);
tws += nw;
if (tws == twr) {
tws = twr = to;
if (ioeof) {
if (shutdown(fd,SHUT_WR) < 0) {
printf("shutdown error: %s\n",strerror(errno));
exit(1);
}
}
}
}
}
}
}
int main(int argc,char** argv) {
if (argc != 3) {
printf("please check <hostname or ip address> <services name or port>\n");
exit(1);
}
struct addrinfo hints;
hints.ai_flags = AI_ALL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo* results;
int err;
if ((err = getaddrinfo(argv[1],argv[2],&hints,&results)) != 0) {
printf("getaddrinfo error: %s\n",gai_strerror(err));
exit(1);
}
struct addrinfo* dummy = results;
int sockfd;
for (; dummy != NULL; dummy = dummy->ai_next) {
if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
continue;
}
if (connect(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
break;
}
close(sockfd);
}
if (dummy == NULL) {
freeaddrinfo(results);
printf("all socket failed\n");
exit(1);
}
freeaddrinfo(results);
client_noblock_echo(sockfd);
return 0;
}
服务端代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#define LISTENQ 100
#define BUFSIZE 4096
void server_echo(int fd) {
char buf[BUFSIZE];
int n;
while ((n = read(fd,buf,BUFSIZE)) > 0) {
if (write(fd,buf,n) != n) {
printf("write to client error: %s\n",strerror(errno));
exit(1);
}
}
}
void sig_child(int signo) {
int pid;
while ((pid = waitpid(-1,NULL,WNOHANG)) > 0) {
printf("child process %d terminated\n",pid);
}
}
int main(int argc,char** argv) {
if (argc != 2) {
printf("please check or add <service name or port>\n");
exit(1);
}
struct addrinfo hints;
bzero(&hints,sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo* results;
int err;
if ((err = getaddrinfo(NULL,argv[1],&hints,&results)) != 0) {
printf("getaddrinfo error: %s\n",gai_strerror(err));
exit(1);
}
struct addrinfo* dummy = results;
int sockfd;
for (; dummy != NULL; dummy = dummy->ai_next) {
if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
continue;
}
if (bind(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
break;
}
close(sockfd);
}
if (dummy == NULL) {
freeaddrinfo(results);
printf("all socket failed\n");
exit(1);
}
freeaddrinfo(results);
if (listen(sockfd,LISTENQ) < 0) {
printf("listen error: %s\n",strerror(errno));
exit(1);
}
if (signal(SIGCHLD,sig_child) == SIG_ERR) {
printf("signal error: %s\n",strerror(errno));
exit(1);
}
int connfd;
int ppid;
for (; ;) {
if ((connfd = accept(sockfd,NULL,NULL)) < 0) {
if (errno == EINTR) {
continue;
}
printf("accept error: %s\n",strerror(errno));
exit(1);
}
if ((ppid = fork()) < 0) {
printf("fork error: %s\n",strerror(errno));
exit(1);
}else if (ppid == 0) {
close(sockfd);
server_echo(connfd);
close(connfd);
exit(0);
}
close(connfd);
}
}
测试:
启动服务器->启动tcpdump捕捉(本次测试服务器是在主机上.所以需要通过网卡lo捕捉,不然是抓不到的)->启动客户端,如图: