一.I/O模型讲解 1)阻塞(block)IO : 当资源不满足条件。 此时进程阻塞。进程休眠,不会浪费CPU。最简单效率低。并且进程最终会阻塞在其中一个阻塞函数上,而其它的函数没法及时调用。
常见的阻塞I/O:read / write fgets/scanf send/recv accept
2)非阻塞的方式调用(noblock) 问题:需要不断轮询每个函数,浪费cpu资源 。若是没有数据,则让进程立即返回错误。 错误码如下: 普通文件描述符 EAGAIN 套接字 EAGAIN or EWOULDBLOCK 错误码路径: /usr/include/asm-generic/errno.h
3)IO多路复用 同时监控多个文件,用一个单进程同时对多路阻塞的IO进行控制,降低系统资源的消耗,提高效率
4)异步信号通知[了解即可] 文件就绪的时候,通过驱动发送信号给应用程序,然后让应用程序对文件进行操作。
二.非阻塞的实现IO
1>文件描述符的属性修改函数
头文件:
open()
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
功能:对文件描述符的一些属性进行设置或者修改。第三个arg参数是否使用由cmd参数的值来决定。
参数:
@ fd 操作的文件描述符
@cmd 操作的指令
F_GETFL (get file) arg被忽略不适用
获得fd的文件标志(O_RDONLY ,O_WRONLY,O_RDWR三者选一被获得, 0,1,2,分别是三者的值 )。
F_SETFL (set file) 通过arg给文件描述符设置状态标志。
arg 可用标志:
O_NONBLOCK(非阻塞的IO,例如read没有文件可读的时候,返回-1和errno置EAGAIN错误)
返回值:若是cmd 为F_GETFL 返回一个相应的文件标志。
若是cmd 为F_SETFL 成功返回0,失败返回-1.
例如:
//把文件描述符设置为非阻塞模式。 int flags = fcntl(fd ,F_GETFL); //获得文件的当前标志位. flags |= O_NONBLOCK. //在当前标志位添加负责非阻塞的标志. fcntl(fd,F_SETFL,flags) ;//重新设置fd的状态标志。
fcntl.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int fd = 0;
int flag = 0;
if(argc < 2)
{
fprintf(stderr,"Usage : %s newfile\n",argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC);
if(fd < 0)
{
perror("Fail to open");
exit(EXIT_FAILURE);
}
flag = fcntl(fd,F_GETFL);
switch(flag)
{
case O_RDONLY:
printf("File flag is O_RDONLY\n");
break;
case O_WRONLY:
printf("File flag is O_WRONLY\n");
break;
case O_RDWR:
printf("File flag is O_RDWR\n");
break;
}
printf("flag : %d\n",flag);
return 0;
}
三.IO多路复用的实现
1.本质: 通过一个单进程创建一个文件描述符表,把多路阻塞的IO存放如表中,进控制。降低系统资源的消耗,提高效率。
2.基本思想 while(1) { 利用selcet函数同时监控多个文件。如果没有文件就绪,就阻塞休眠。只要有一个文件就绪,就返回找出就绪的文件,然后处理就绪的文件。 }
常用函数:
1.头文件
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
功能:实现IO多路复用,检测指定的文件描述符
参数:
@ nfds 文件描述符的个数 (最大的文件描述符maxfd + 1)
@ readfsd 监控的"读操作"文件描述符表
@ writefds 监控的"写操作"文件描述符表
@ exceptfds 其它的文件描述符集合 (一般是指异常的文件描述符集合)
@ timeout NULL 阻塞方式调用
struct timeval结构体中的数据为 0 超时时间设置为0s,
非阻塞的方式调用
自己指定超时时间 指定的时间内等待
struct timeval{
long tv_sec; //设置秒数
long tv_usec; //设置微秒
}
返回值:
成功 , 若是返回值>0 返回就绪的文件的描述符的个数,并且在文件描述符表中清除其他未就绪的文件描述符。
若是返回值=0 , 连接超时
错误,返回-1, 并置errno
例如: //设置5s中的超时时间 struct timeval tm = {5,0}; //设置8000微秒的超时时间 struct timeval tm = {0,800}
select (…,&tm)
思考:为什么我们的select第一个参数是最大的文件描述符 + 1呢?
typedef struct{
long int fds_bits[1024 / 32];
}fd_set;
FD_ISSET
注意:fd_set 结构体 实质为一个long类似的数组每一个数组元素都能与一打开的socket文件描述符建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件发生了可读或可写事件。
//将fd 从 set 的文件描述符数组集合中清除 [file descriptor clear]
void FD_CLR(int fd, fd_set *set);
//判断 fd在set的文件描述符数组集合中是否已被设置(就绪) [file descriptor set]
int FD_ISSET(int fd, fd_set *set);
//将fd添加到 set对应的文件描述符数组集合中
void FD_SET(int fd, fd_set *set);
//将set文件描述符数组集合清零,
void FD_ZERO(fd_set *set);
例如: 假如我们的服务端想要从键盘输入数据,并且还能接受用户的连接。我们就可以进行如下操作: 利用监测listen_fd STDIN_FILENO 它们是否进行了读操作 <1>建立文件描述符表
fd_set readfds;
fd_set readfds_bak;
FD_ZERO(&readfds_bak);
FD_SET(STDIN_FILENO,&readfds_bak);
FD_SET(listenfd,&readfds_bak);
maxfd = listen_fd > STDIN_FILENO ? listenfd :STDIN_FILENO;
while(1)
{
readfs = readfds_bak;
n = select(maxfd + 1, &readfds,NULL,NULL,NULL);
for(i = 0;i < =maxfd,;i++)
{
if(FD_ISSET(i,readfs))
{
if(i === STDIN_FILENO)
{
fgets(buf,sizeof(buf),stdin);
fputs(buf,stdout);
}else if(i == listenfd)
{
connect_fd = accept();
}
}
}
}
练习:select函数应用于tcp服务器 1.监测标准输入,listen_fd,若标准输入就绪,读数据并打印,若有新连接请求,提取并打印请求方ip和端口如果有用户输入数据,则接收用户数据并打印出来。 server.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define N 1024
int main(int argc, const char *argv[])
{
char buf[1024] = {0};
int listen_fd = 0;
int connect_fd = 0;
struct sockaddr_in my_addr;
struct sockaddr_in client_addr;
int len = sizeof(my_addr);
int n = 0 ;
fd_set rfds;
fd_set rfds_bak;
int maxfd = 0;
int i = 0;
if(argc < 3)
{
fprintf(stderr,"Usage : %s ip port!\n",argv[0]);
exit(EXIT_FAILURE);
}
listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(listen_fd < 0)
{
perror("Fail to socket");
exit(EXIT_FAILURE);
}
memset(&my_addr,0,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
my_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(listen_fd,(struct sockaddr *)&my_addr,len) < 0)
{
perror("Fail to bind");
exit(EXIT_FAILURE);
}
listen(listen_fd,5);
printf("Listen....\n");
FD_ZERO(&rfds_bak);
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO,&rfds_bak);
FD_SET(listen_fd,&rfds_bak);
maxfd = listen_fd > STDIN_FILENO ? listen_fd : STDIN_FILENO;
while(1)
{
rfds = rfds_bak;
n = select(maxfd + 1,&rfds,NULL,NULL,NULL);
if(n < 0)
{
perror("Fail to select");
exit(EXIT_FAILURE);
}
for(i = 0;i <= maxfd;i++)
{
if(FD_ISSET(i,&rfds))
{
if(i == STDIN_FILENO)
{
putchar('>');
fgets(buf,sizeof(buf),stdin);
printf("buf : %s\n",buf);
}else if(i == listen_fd){
connect_fd = accept(listen_fd,(struct sockaddr *)&client_addr,&len);
if(connect_fd < 0)
{
perror("Fail to accept");
exit(EXIT_FAILURE);
}
printf("=============================================");
printf("connect_fd : %d\n",connect_fd);
printf("client IP : %s\n",inet_ntoa(client_addr.sin_addr));
printf("client port : %d\n", ntohs(client_addr.sin_port));
printf("=============================================");
FD_SET(connect_fd,&rfds_bak);
maxfd = connect_fd > maxfd ? connect_fd : maxfd;
}
}
}
}
close(listen_fd);
close(connect_fd);
exit(EXIT_SUCCESS);
}
client.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#define N 1024
int main(int argc, const char *argv[])
{
char buf[1024] = {0};
int sockfd = 0;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int len = sizeof(server_addr);
int n = 0 ;
if(argc < 3)
{
fprintf(stderr,"Usage : %s ip port!\n",argv[0]);
exit(EXIT_FAILURE);
}
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("Fail to socket");
exit(EXIT_FAILURE);
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sockfd,(struct sockaddr *)&server_addr,len) < 0)
{
perror("Fail to accept");
exit(EXIT_FAILURE);
}
while(1)
{
memset(buf,0,sizeof(buf));
putchar('>');
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf) - 1] = '\0';
n = send(sockfd,buf,strlen(buf),0);
if(n < 0)
{
perror("Fail to recv!\n");
exit(EXIT_FAILURE);
}else if(n == 0){
printf("clinet is not connect\n");
exit(EXIT_FAILURE);
}
if(strncmp(buf,"quit",4) == 0)
break;
}
close(sockfd);
exit(EXIT_SUCCESS);
}
四.设置,获得socket属性
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
功能:获得或者设置socket属性
参数:
@sockfd 指定socket
@level 指定操作socket层次
可选SOL_SOCKET
IPPROTO_IP
IPPROTO_TCP
@optname 由level来选择控制的方式
SOL_SOCKET :SO_BROADCAST 允许发送广播包
:SO_RCVTIMEO 接收超时
:SO_KEEPALIVE 保持连接
@optval 设置相应控制方式的值,定义变量传地址
@optlen optval之的大小
返回值:成功返回0;失败返回-1,置errno
例如:
设置5s的超时
struct timeval tm = {5,0};
if (setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tm,sizeof(tm)) < 0)
{
perror("setsockopt fail");
exit(EXIT_FAILURE);
}
网络超时检测的方法 1>设置socket超时
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
socket 用sockfd
socklen_t len = sizeof(struct timeval);
struct timeval tv;
tv.tv_sec = 5; //超时5s
tv.tv_usec = 0;
//检测5s时间的超时检测
if(setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tv,len) < 0)
handle_error();
Service.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
int main(int argc, const char *argv[])
{
int sockfd = 0;
struct sockaddr_in my_addr;
struct sockaddr_in client_addr;
socklen_t len = sizeof(my_addr);
int n = 0;
char buf[1024] = {0};
struct timeval tv = {5,0};
if(argc < 3)
{
fprintf(stderr,"Usage : %s ip port!\n",argv[0]);
exit(EXIT_FAILURE);
}
sockfd = socket(AF_INET,SOCK_DGRAM ,0);
if(sockfd < 0)
{
perror("Fail to socket!");
exit(EXIT_FAILURE);
}
memset(&my_addr,0,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
my_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(sockfd,(struct sockaddr *)&my_addr,len) < 0)
{
perror("Fail to bind");
exit(EXIT_FAILURE);
}
if(setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) < 0)
{
perror("Fail to setsocketopt");
exit(EXIT_FAILURE);
}
while(1)
{
memset(buf,0,sizeof(buf));
n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&client_addr,&len);
if(n < 0)
{
perror("recvfrom timeout deal with....\n");
continue;
}else{
printf("===============================\n");
printf("Recv from IP : %s\n",inet_ntoa(client_addr.sin_addr));
printf("Recv fromt port : %d\n", ntohs(client_addr.sin_port));
printf("Recv %d bytes : %s\n",n,buf);
}
}
return 0;
}
client.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int sockfd = 0;
struct sockaddr_in server_addr;
socklen_t len = sizeof(server_addr);
int n = 0;
char buf[1024] = {0};
if(argc < 3)
{
fprintf(stderr,"Usage : %s ip port!\n",argv[0]);
exit(EXIT_FAILURE);
}
sockfd = socket(AF_INET,SOCK_DGRAM ,0);
if(sockfd < 0)
{
perror("Fail to socket!");
exit(EXIT_FAILURE);
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
while(1)
{
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf) - 1] = '\0';
n = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&server_addr,len);
if(n < 0)
{
perror("Fail to recvfrom");
exit(EXIT_FAILURE);
}
}
return 0;
}
每5s中如果没有输入的话就会出现出错提醒。
2>select函数设置超时检测
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
socket由sockfd标识
例:
fd_set rfds,fds_bak;
FD_ZERO(&fds_bak);
FD_SET(&sockfd,&fds_bak);
struct timeval tv = {5,0};
Service.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define N 1024
int main(int argc, const char *argv[])
{
char buf[1024] = {0};
int listen_fd = 0;
int connect_fd = 0;
struct sockaddr_in my_addr;
struct sockaddr_in client_addr;
int len = sizeof(my_addr);
int n = 0 ;
struct timeval tv = {10, 0};
struct timeval *p = NULL;
p = &tv;
fd_set rfds;
fd_set rfds_bak;
int maxfd = 0;
int i = 0;
if(argc < 3)
{
fprintf(stderr,"Usage : %s ip port!\n",argv[0]);
exit(EXIT_FAILURE);
}
listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(listen_fd < 0)
{
perror("Fail to socket");
exit(EXIT_FAILURE);
}
memset(&my_addr,0,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
my_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(listen_fd,(struct sockaddr *)&my_addr,len) < 0)
{
perror("Fail to bind");
exit(EXIT_FAILURE);
}
listen(listen_fd,5);
printf("Listen....\n");
FD_ZERO(&rfds_bak);
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO,&rfds_bak);
FD_SET(listen_fd,&rfds_bak);
maxfd = listen_fd > STDIN_FILENO ? listen_fd : STDIN_FILENO;
while(1)
{
rfds = rfds_bak;
n = select(maxfd + 1,&rfds,NULL,NULL,p);
if(n < 0)
{
perror("Fail to select");
exit(EXIT_FAILURE);
} else if (n == 0) {
printf("select will timeout!\n");
sleep(1);
p->tv_sec = 10;
p->tv_usec = 0;
}
for(i = 0;i <= maxfd;i++)
{
if(FD_ISSET(i,&rfds))
{
if(i == STDIN_FILENO)
{
putchar('>');
fgets(buf,sizeof(buf),stdin);
printf("buf : %s\n",buf);
}else if(i == listen_fd){
connect_fd = accept(listen_fd,(struct sockaddr *)&client_addr,&len);
if(connect_fd < 0)
{
perror("Fail to accept");
exit(EXIT_FAILURE);
}
printf("=============================================");
printf("connect_fd : %d\n",connect_fd);
printf("client IP : %s\n",inet_ntoa(client_addr.sin_addr));
printf("client port : %d\n", ntohs(client_addr.sin_port));
printf("=============================================");
FD_SET(connect_fd,&rfds_bak);
maxfd = connect_fd > maxfd ? connect_fd : maxfd;
} else {
memset(buf, 0, sizeof(buf));
n = recv(i, buf, sizeof(buf), 0);
if (n < 0) {
perror("Fail to recv");
exit(EXIT_FAILURE);
} else if (n == 0) {
printf("client no connect\n");
exit(EXIT_FAILURE);
}
printf("Recv %d bytes: %s\n", n, buf);
if (strncmp(buf, "quit", 4) == 0) {
FD_CLR(i, &rfds_bak);
close(i);
}
}
}
}
}
close(listen_fd);
close(connect_fd);
exit(EXIT_SUCCESS);
}
rfds = fds_bak; select(maxfd + 1,&rfds,NULL,NULL,&tv); //设置5s的超时,非阻塞
3> 超时机制的常用实例。针对不同的网络异常进行处理
- 客户端出了问题(死机、重启了、网络断了…)
- 服务端出了问题(死机、重启了、网络断了、内存耗尽、…)
- 网络不正常
解决方法:设置心跳包。
Keepalive,是TCP中一个可以检测死TCP连接的机制。原理很简单,TCP协议会在空闲了一定的时间之后发送数据给对方。如果对方在一定的时间内没有回应,则表示超时,撤销连接。
typedef struct
{
int type; //发送包的类型,数据包,还是心跳包
char buf[1024]; //发送的数据
}
客户端: 创建子线程每过1s发送一个心跳包,主线程正常从键盘输入数据发送。 心跳包类型 MSG msg = {HEART_PACKET,“I am alive!”}; 服务器端: 客气子进程进行接收客户端消息。通过alarm设置定时,收到正常包正常打印,收到心跳包重新打印数据,若是一定时间内没有收到内没有收到心跳包,就结束当前进程。
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define N 1024
typedef struct {
int type;
char buf[1024];
} msg_t;
#define DATA_PACKET 10
#define HEADT_PACKET 20
void handle_alarm() {
printf("the GAME OVER!\n");
exit(EXIT_SUCCESS);
return;
}
void do_client(int sockfd) {
msg_t msg;
int n = 0;
if (signal(SIGALRM, handler_alarm) == SIG_ERR) {
perror("Fail to signal");
exit(EXIT_FAILURE);
}
alarm(10);
while(1) {
memset(&msg, 0, sizeof(msg));
if ((n = recv(sockfd, &msg, sizeof(msg), 0)) > 0)
{
if (msg.type == DATA_PACKET) {
printf("Recv %d bytes : %s\n", strlen(msg.buf), msg.buf);
continue;
} else if (msg.type == HEADT_PACKET) {
printf("head recv : %s\n", msg.buf);
alarm(10);
}
if (strncmp(msg.buf, "quit", 4) == 0)
break;
}
exit(EXIT_SUCCESS);
}
}
void handler_process(int signum) {
waitpid(-1, NULL, WNOHANG);
printf("child exit is success!\n");
return;
}
int main(int argc, const char *argv[])
{
char buf[1024] = {0};
int listen_fd = 0;
int connect_fd = 0;
struct sockaddr_in my_addr;
struct sockaddr_in client_addr;
int len = sizeof(my_addr);
int n = 0 ;
pid_t pid;
int ret =0;
struct timeval tv = {10, 0};
struct timeval *p = NULL;
p = &tv;
fd_set rfds;
fd_set rfds_bak;
int maxfd = 0;
int i = 0;
if(argc < 3)
{
fprintf(stderr,"Usage : %s ip port!\n",argv[0]);
exit(EXIT_FAILURE);
}
if (signal(SIGCHLD, handler_process) == SIG_ERR) {
perror("Fail to signal");
exit(EXIT_FAILURE);
}
listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(listen_fd < 0)
{
perror("Fail to socket");
exit(EXIT_FAILURE);
}
memset(&my_addr,0,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
my_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(listen_fd,(struct sockaddr *)&my_addr,len) < 0)
{
perror("Fail to bind");
exit(EXIT_FAILURE);
}
listen(listen_fd,5);
printf("Listen....\n");
FD_ZERO(&rfds_bak);
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO,&rfds_bak);
FD_SET(listen_fd,&rfds_bak);
maxfd = listen_fd > STDIN_FILENO ? listen_fd : STDIN_FILENO;
while(1)
{
rfds = rfds_bak;
n = select(maxfd + 1,&rfds,NULL,NULL,p);
if(n < 0)
{
perror("Fail to select");
exit(EXIT_FAILURE);
} else if (n == 0) {
printf("select will timeout!\n");
sleep(1);
p->tv_sec = 10;
p->tv_usec = 0;
}
for(i = 0;i <= maxfd;i++)
{
if(FD_ISSET(i,&rfds))
{
if(i == STDIN_FILENO)
{
putchar('>');
fgets(buf,sizeof(buf),stdin);
printf("buf : %s\n",buf);
}else if(i == listen_fd){
connect_fd = accept(listen_fd,(struct sockaddr *)&client_addr,&len);
if(connect_fd < 0)
{
perror("Fail to accept");
exit(EXIT_FAILURE);
}
printf("=============================================");
printf("connect_fd : %d\n",connect_fd);
printf("client IP : %s\n",inet_ntoa(client_addr.sin_addr));
printf("client port : %d\n", ntohs(client_addr.sin_port));
printf("=============================================");
FD_SET(connect_fd,&rfds_bak);
maxfd = connect_fd > maxfd ? connect_fd : maxfd;
} else {
memset(buf, 0, sizeof(buf));
n = recv(i, buf, sizeof(buf), 0);
if (n < 0) {
perror("Fail to recv");
exit(EXIT_FAILURE);
} else if (n == 0) {
printf("client no connect\n");
exit(EXIT_FAILURE);
}
printf("Recv %d bytes: %s\n", n, buf);
if (strncmp(buf, "quit", 4) == 0) {
FD_CLR(i, &rfds_bak);
close(i);
}
}
}
}
}
close(listen_fd);
close(connect_fd);
exit(EXIT_SUCCESS);
}
|