#include
#include
#include
int select(int n, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout);
参数:
maxfd
所有监控的文件描述符中最大的那一个加1
read_fds
所有要读的文件文件描述符的集合
write_fds
所有要的写文件文件描述符的集合
except_fds
其他要向我们通知的文件描述符
timeout
超时设置.
Null:一直阻塞,直到有文件描述符就绪或出错
时间值为0:仅仅检测文件描述符集的状态,然后立即返回
时间值不为0:在指定时间内,如果没有事件发生,则超时返回。
在我们调用select时进程会一直阻塞直到以下的一种情况发生.
–有文件可以读.
–有文件可以写.
–超时所设置的时间到.
为了设置文件描述符我们要使用几个宏:
宏的形式:
void FD_ZERO(fd_set *fdset)
void FD_SET(int fd,fd_set *fdset)
void FD_CLR(int fd,fd_set *fdset)
int FD_ISSET(int fd,fd_set *fdset)
–FD_SET 将fd加入到fdset
–FD_CLR 将fd从fdset里面清除
–FD_ZERO 从fdset中清除所有的文件描述符
–FD_ISSET 判断fd是否在fdset集合中
服务器server.c
#include "Network.h"
#define MAX_FD 10
int main(int argc, const char **argv)
{
int fd = -1, newfd = -1, ret;
struct sockaddr_in server_addr;
int i = 0;
char buf[BUFSIZ];
/*1、创建套接字*/
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return -1;
}
/*2、填充网络信息结构体*/
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
//优化:允许任意IP地址的客户端连接
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
/*3、绑定=====将名称分配给套接字*/
if(bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("bind");
return -1;
}
/*4、监听=====把主动套接字转换为被动套接字*/
if(listen(fd, 5) < 0)
{
perror("listen");
return -1;
}
printf("***Server start success***\n");
fd_set rset, temps;
int num;
struct sockaddr_in client_addr;
socklen_t AddrLen = sizeof(client_addr);
char ipv4_addr[16];
int fd_all[MAX_FD]; //保存所有描述符,用于select调用后,判断哪个可读
memset(fd_all, -1, sizeof(fd_all));
fd_all[0] = fd; //第一个为监听套接字
FD_ZERO(&rset); //清零
FD_SET(fd, &rset); //监听套接字fd加进集合,监视fd
int maxfd = fd_all[0]; //监听的最大套接字
/*
5、用Select函数,达到I/O多路复用,一个服务器支持多个客户端
*/
while(1)
{
temps = rset; //因为无事件发生的fd会被清空,需要重新复制
if((num = select(maxfd + 1, &temps, NULL, NULL, NULL)) < 0)
{
perror("select");
return -1;
}
if(num < 0)
{
perror("select");
return -1;
}
if(num == 0)
{
printf("timeout\n");
}
if(FD_ISSET(fd, &temps)) //监听套接字如果可读,就说明有新客户端连接进来了
{
/*
6、接受=====阻塞等待客户端连接
*/
if((newfd = accept(fd, (struct sockaddr *)&client_addr, &AddrLen)) < 0)
{
perror("accept");
close(fd);
return -1;
}
//客户端连接成功后在服务器端打印出来
if(!inet_ntop(AF_INET, (void *)&client_addr.sin_addr.s_addr, ipv4_addr, sizeof(client_addr)))
{
perror("ionet_ntop");
close(newfd);
close(fd);
return -1;
}
printf("Client [%s:%d] connected!\n", ipv4_addr, htons(client_addr.sin_port));
/* 将新连接套接字加入进fd_all集合 和 rset集合 */
for(i = 0;i <= MAX_FD; i++)
{
if(fd_all[i] == -1)
{
fd_all[i] = newfd;
printf("client fd_all[%d] join in.\n", i);
break;
}
else
{
continue;
}
}
if(newfd == FD_SETSIZE)
{
printf("error: too many client newfd\n");
return -1;
}
if(newfd > -1 && newfd < FD_SETSIZE)
{
FD_SET(newfd, &rset);
}
if(maxfd < newfd)
{
maxfd = newfd; //更新maxfd
}
}
/*
7、读写数据,并且将数据返回给客户端
循环判断哪个文件描述符
*/
for(int i = 1;i < maxfd; i++)
{
if(FD_ISSET(fd_all[i], &temps))
{
bzero(buf, BUFSIZ);
do {
ret = read(fd_all[i], buf, BUFSIZ-1);
}while(ret < 0 && EINTR == errno);
if(ret < 0)
{
perror("read");
return -1;
}
if(!ret) //客户端退出,关闭套接字,并从监听集合清除
{
FD_CLR(fd_all[i], &rset);
close(fd_all[i]);
fd_all[i] = -1;
continue;
}
if(!strncasecmp(buf, "quit", 4))
{
printf("Client all_fd[%d] has left!\n", i);
continue;
}
printf("[Server]Receive data: %s\n",buf);
}
}
}
return 0;
}
客户端client.c
#include "Network.h"
int main(int argc, const char **argv)
{
int fd = -1, ret;
struct sockaddr_in server_addr;
char buf[BUFSIZ];
/*1、创建套接字*/
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return -1;
}
/*2、填充网络信息结构体*/
bzero(&server_addr, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
#if 0
//简单写法(只适用于IPv4)
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
#else
//复杂写法(也兼容IPv6)
if(inet_pton(AF_INET, SERVER_IP, (void *)&server_addr.sin_addr.s_addr) != 1)
{
perror("inet_pton");
return -1;
}
#endif
/*3、与服务器建立连接*/
if(connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("connect");
return -1;
}
printf("Connect to the server!\n");
/*4、
用Select函数,达到I/O多路复用,一个服务器支持多个客户端
读写=====发送信息给服务器端
*/
fd_set rset;
int maxfd = -1;
while(1)
{
FD_ZERO(&rset);
FD_SET(0, &rset);
FD_SET(fd, &rset);
maxfd = fd;
if(select(maxfd + 1, &rset, NULL, NULL, NULL) < 0)
{
perror("select");
return -1;
}
if (FD_ISSET (0, &rset)) { //标准键盘上有输入
//读取键盘输入,发送到网络套接字fd
bzero (buf, BUFSIZ);
do {
ret = read (0, buf, BUFSIZ - 1);
} while (ret < 0 && EINTR == errno);
if (ret < 0) {
perror ("read");
continue;
}
if (!ret)
continue;
if (write (fd, buf, strlen (buf)) < 0) {
perror ("write() to socket");
continue;
}
if (!strncasecmp(buf, "quit", 4)) { //用户输入了quit字符
printf ("Client is exiting!\n");
break;
}
}
}
close(fd);
return 0;
}
头文件Network.h
#ifndef __NETWORK_H__
#define __NEWWORK_H__
#include
#include
#include
#include
#include
#include /* See NOTES */
#include
#include
#include
#include
#define SERVER_IP "10.172.154.133"
#define SERVER_PORT 5520
#endif