#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct socket_t
{
struct sockaddr_in cli_addr;
int cli_fb;
};
int num; // 在线人数
int count; // 掉线线人数
struct socket_t people[10];
int main(int argc, char const *argv[])
{
// 1、创建socket
int fb = socket(AF_INET, SOCK_STREAM, 0);
if (fb < 0)
{
perror("socket");
return 0;
}
// 绑定地址
struct sockaddr_in addr; // 定义结构体
addr.sin_family = AF_INET; // 设置为IPV4
addr.sin_port = htons(8966); // 设置端口号为8080 htons():将本地网络字序改为网络传输统一的大端存储的字序
// ntohs() 将网络字序转化为本地主机字序
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 设置网络IP inet_addr():将字符串转化为整型地址
// 2、绑定socket
if (bind(fb, (struct sockaddr *)&addr, sizeof(addr)) == 0)
{
perror("bind");
}
else
{
perror("bind fail");
}
// 3、设置监听,,模式
if (listen(fb, 10))
{
perror("listen");
}
int i = 0;
num = 0;
count = fb;
printf("输入文件描述符进行通信\n");
while (1)
{
// 多路复用
fd_set rfbs;
// 清空集合
FD_ZERO(&rfbs);
// 添加服务器描述符
FD_SET(fb, &rfbs);
//添加标志输入描述符
FD_SET(0, &rfbs);
for (int x = 0; x < 10; x++)
{
if (people[x].cli_fb != 0)
{
printf("在线人员描述符:%d\n", people[x].cli_fb);
//将连接的客户机加入监听
FD_SET(people[x].cli_fb, &rfbs);
}
}
// 设置超时连接时间
// struct timeval timeout;
// timeout.tv_sec = 10;
// timeout.tv_usec=0;
// 开始监听服务器
char msg[521];
int ret = select(count + 1, &rfbs, NULL, NULL, NULL);
if (ret > 0)
{
printf("有活跃fb\n");
if (FD_ISSET(fb, &rfbs))
{
printf("有人连接\n");
printf("已连接%d人", num + 1);
for (i = 0; i < 10; i++)
{
if (people[i].cli_fb == 0)
{
break;
}
}
socklen_t len = sizeof(people->cli_addr);
people[i].cli_fb = accept(fb, (struct sockaddr *)&people[i].cli_addr, &len);
count = people[i].cli_fb;
num++;
}
for (int j = 0; j < 10; j++)
{
if (people[j].cli_fb == 0)
{
continue;
}
if (FD_ISSET(people[j].cli_fb, &rfbs))
{
int temp = read(people[j].cli_fb, msg, sizeof(msg));
if (temp <= 0)
{
//将掉线的客户机移出监听
FD_CLR(people[j].cli_fb, &rfbs);
num--;
printf("%d掉线\n", people[j].cli_fb);
memset(&people[j], 0, sizeof(struct socket_t));
}
else
{
printf("收到%d的新消息%s\n", people[j].cli_fb, msg);
break;
}
}
if (msg[0] == '1')
{
break;
}
if (FD_ISSET(0, &rfbs))
{
int op;
scanf("%d", &op);
memset(msg, 0, sizeof(msg));
printf("请输入信息\n");
scanf("%s", msg);
write(op, msg, strlen(msg));
}
}
}
}
for (int a = 0; a < 10; a++)
{
if (people[a].cli_fb == 0)
{
continue;
}
close(people[a].cli_fb);
}
close(fb);
return 0;
}
这这段代码主要实现TCP服务端时时监听客户机的连接,用实现一对多通行;
SELECT(2) Linux Programmer's Manual SELECT(2)
NAME
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing
SYNOPSIS
/* According to POSIX.1-2001, POSIX.1-2008 */
#include
/* According to earlier standards */
#include
#include
#include
#include
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
功能;多路复用,同时监听多个阻塞的函数,并在其被唤醒时做对应的处理。
nfds:监听的描述符中最大值加1;
readfds:监听集合;
readfds: 指定要让内核测试读条件的 fd 集合,如果不需要或不关心此类事件的话,可设置为NULL;
writefds: 指定要让内核测试写条件的 fd 集合,如果不需要或不关心此类事件的话,可设置为NULL;
exceptfds: 指定要让内核测试异常条件的 fd 集合,如果不需要或不关心此类事件的话,可设置为NULL;
timeout: 设置select的超时时间;
timeout通常有以下三种设置方式:
NULL:阻塞式等待,select将一直被阻塞,直到某个文件描述符上发送了事件;
0:非阻塞式等待,调用select后检测文件描述符的状态,然后立即返回;
特定的时间值:阻塞式等待一定时间,若期间没有事件发生,select将超时返回
void FD_CLR(int fd, fd_set *set);
功能:移除监听文件描述符
参数: fb:需要移除的描述符fb
fd_set *set:监听集合;
int FD_ISSET(int fd, fd_set *set);
功能:处理活跃的文件描述符
参数: fb:监听到活跃的文件描述符fb;
fd_set *set:监听集合;
void FD_SET(int fd, fd_set *set);
功能:加入监听描述符
参数:fb:需要加入监听的文件描述
fd_set *set:监听集合;
void FD_ZERO(fd_set *set);
功能:清空监听描述符
参数: fd_set *set:监听集合;