No.1 select
select:轮询
轮流检查描述符号集合中的所有描述符号 是否有动静(serverFd 是否可以读-有客户端连接
clientSocket是否可以读 - 客户端有发送数据)
有动静才调用阻塞函数,这样阻塞函数不会再阻塞。
用select的阻塞 代替了多个读fd的函数阻塞
编程模型:
1. 创建描述符号集合
2. 把要监视的描述符号放入描述符号集合当中
3. 监视 select
4. 根据select的返回值来做对应操作
-1 : 失败
0 : 没有动静
正整数:有动静 根据返回值来做对应操作
有动静后fd_set里面会清空没有动静的文件描述符号,只留下有动静的文件描述符号
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int select(
int nfds, //表示集合中所有文件描述符的范围,即所有文件描述符的最大值+1。
fd_set *readfds, //监视输入的描述符号集合
fd_set *writefds, // 输出
fd_set *exceptfds, // 错误输出
struct timeval *timeout);//延时设置
void FD_CLR(int fd, fd_set *set); //删除某一个
int FD_ISSET(int fd, fd_set *set); //判断是否再监控里
void FD_SET(int fd, fd_set *set); //设置增加到里里面
void FD_ZERO(fd_set *set);*/ //类似信号屏蔽的
No.2 TCP多客户端群聊
Server
使用父子进程,一个专门用来发消息,一个专门用来收来自服务器的消息
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#define Num 256
int main()
{
int ClientSocket[Num];
//1. 创建socket
int clientFd = socket(AF_INET, SOCK_STREAM, 0);
if (clientFd==-1) printf("【客户端】创建socket失败!%m\n"),exit(-1);
printf("【客户端】创建socket成功%m clientFd:%d!\n",clientFd);
//2. 创建协议地址簇
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET; //与socket的第一个参数一致
addr.sin_addr.s_addr = inet_addr("127.0.0.1");//字符串转为整形 需要注意大小端的问题
addr.sin_port = htons(12357);//端口号
//3. 绑定
int r = connect(clientFd,(struct sockaddr*)&addr,sizeof addr);
if(r==-1) printf("【客户端】链接%s失败!\n",inet_ntoa(addr.sin_addr)),exit(-1);
printf("【客户端】链接%s成功\n",inet_ntoa(addr.sin_addr));
if(fork())//父进程等待终端输入
{
while(1)
{
char buff[1024];
memset(buff,0,1024);
printf("【客户端】输入<<");
scanf("%s",buff);
r = strlen(buff);
r = send(clientFd,buff,r,0);
if(r==-1)
{
printf("【客户端】发送消息失败!\n");
continue;
}
else printf("【客户端】发送成功!\n");
}
}
else//子进程等待服务器发送
{
char temp[1024];
while(1)
{
memset(temp,0,1024);
r = recv(clientFd,temp,1023,0);
if(r>0)
{
temp[r] = 0;
printf("【服务器】%s\n",temp);
}
}
}
return 0;
}
Server
使用select来判断y有动静的文件描述符号,然后根据文件描述符号看是服务器产生的反应还是客户端产生的反应
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define Num 256
int main()
{
//1. 创建socket
int serverFd = socket(AF_INET, SOCK_STREAM, 0);
if (serverFd==-1) printf("【服务器】创建socket失败!%m\n"),exit(-1);
printf("【服务器】创建socket成功%m serverFd:%d!\n",serverFd);
//2. 创建协议地址簇
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET; //与socket的第一个参数一致
addr.sin_addr.s_addr = inet_addr("127.0.0.1");//字符串转为整形 需要注意大小端的问题
addr.sin_port = htons(12357);//端口号
//3. 绑定
int r = bind(serverFd,(struct sockaddr*)&addr,sizeof addr);
if (r==-1) printf("【服务器】绑定sockaddr失败!%m\n"),exit(-1);
printf("【服务器】绑定sockaddr成功%m!\n");
//4. 监听
r = listen(serverFd,10);
if (r==-1) printf("【服务器】监听sockaddr失败!%m\n"),exit(-1);
printf("【服务器】监听sockaddr成功%m!\n");
//保存所有的连到的客户端的套接字
int ClientSocket[Num];
for(int i=0;i<Num;i++) ClientSocket[i]=-1;
//加入监视
fd_set fds;//描述符号集合
FD_ZERO(&fds);
int cnt=0; //记录当前连接客户端的数量
int maxFd=0;//记录最大的描述符号值
//接受客户端连接
struct sockaddr_in cAddr = {0};
int cLen = sizeof(cAddr);
int cfd;
char buff[1024];
char temp[1024];
maxFd = ((maxFd>serverFd)?maxFd:serverFd);
while(1)
{
cfd=-1;
FD_ZERO(&fds);//清空
FD_SET(serverFd,&fds);//把服务器的放进去
//把所有的客户端的套接字放进去
for(int i=0;i<Num;i++)
if(ClientSocket[i]!=-1)
FD_SET(ClientSocket[i],&fds);
r = select(maxFd+1,&fds,NULL,NULL,NULL);
if(r==-1)
{
printf("【服务器】服务器崩溃%m\n");
close(serverFd);
return -1;
}
else if(r==0)
{
printf("【服务器】正在努力加载中..\n");
continue;
}
else
{
//检查服务器有没有反应
if(FD_ISSET(serverFd,&fds))
{
cfd = accept(serverFd,(struct sockaddr*)&cAddr,&cLen);
if(-1==cfd)
{
printf("客户端连接失败!%m\n");
}
else
{
printf("(%s)连接上服务器了,它的套接字为:%d!\n",inet_ntoa(cAddr.sin_addr),cfd);
//保存客户端的套接字
for(int i=0;i<Num;i++)
{
if(-1 == ClientSocket[i])
{
ClientSocket[i]=cfd;
maxFd = ((maxFd>cfd)?maxFd:cfd);
break;
}
}//endif
}//endelse //客户端连接成功
}//endif 检查服务器有没有反应
//检查客户端是否有动静
for(int i=0;i<Num;i++)
{
if(-1!=ClientSocket[i]&&
FD_ISSET(ClientSocket[i],&fds))
{
r = recv(ClientSocket[i],buff,1023,0);
if(r>0)
{
buff[r] = 0;
printf("【客户端】%d>>%s\n",ClientSocket[i],buff);
//发送给当前连上服务器的每一个客户端
memset(temp,0,1024);
sprintf(temp,"(客户端%d)>>%s",ClientSocket[i],buff);
//转发给每一个客户端
for(int j=0;j<Num;j++)
{
if(-1!=ClientSocket[j]&&
ClientSocket[i]!=ClientSocket[j])
{
send(ClientSocket[j],temp,strlen(temp),0);
}
}
}
else
{
printf("%d客户端断开连接!\n",ClientSocket[i]);
ClientSocket[i]=-1;
}
}//判断是某一个客户端有反应了
}
}//有反应了
}//end While(1)
return 0;
}
效果图