一、实现2个客户端之间互相聊天
要求:服务器使用 select 模型实现接受多个客户端连接,以及转发消息 客户端要求:使用 poll 模型解决 技能够 read 读取服务器发来的消息,又能够scanf读取键盘输入的信息 客户端服务器不允许开启额外线程和进程
服务端代码
#include <25041head.h>
struct Client
{
int fd; //发送者id端口号
int id; //接收者端口号
char mtext[1024]; //发送的消息内容
};
struct Client_arr
{
int fd;
int id;
};
void insert_client(struct Client_arr *client_arr,int *client_arr_len,int newclient)
{
client_arr[*client_arr_len].fd=newclient;
client_arr[*client_arr_len].id=*(client_arr_len);
(*client_arr_len)++;
}
void rm_client(struct Client_arr *client_arr,int *client_arr_len,int client)
{
int i=0,j=0;
for(i=0;i<*client_arr_len;i++)
{ struct Client new_client;
if(client_arr[i].fd==client)
break;
}
for(j=i;j<*client_arr_len-1;j++)
{
client_arr[j]=client_arr[j+1];
}
(*client_arr_len)--;
}
int main(int argc, const char *argv[])
{
if(argc<2)
{
printf("请输入端口号!\n");
return 1;
}
short port=atoi(argv[1]);
//创建服务器套接字
int server=socket(AF_INET,SOCK_STREAM,0);
//准备好要写入的套接字信息结构体,初始化信息
struct sockaddr_in addr={0};
//设置地址族为ipv4
addr.sin_family=AF_INET;
//设置监听端口号
addr.sin_port=htons(port);
//设定接收的ip地址,0为接收所有
addr.sin_addr.s_addr=inet_addr("0.0.0.0");
//将结构体信息(ip和port)写入套接字中
if(bind(server,(struct sockaddr*)&addr,sizeof(addr))==-1)
{
perror("bind error..");
return -1;
}
//监听
listen(server,10);
fd_set list; //创建监视列表
FD_ZERO(&list); //初始化信息
FD_SET(server,&list); //服务器写入监视列表中
FD_SET(0,&list); //监视输入流
int fd_count=3;
//申请数组存客户端套接字
struct Client client_msg={0};
struct Client_arr client_arr[100]={0};
int client_arr_len=0;
while(1)
{
//备份监视列表,select激活列表和监视列表是同一张
fd_set backup=list;
int fd;
select(fd_count+1,&backup,NULL,NULL,NULL); //list是监视列表,backup是激活列表
if(FD_ISSET(server,&backup))
{
//接收
//准备好客户端结构体
struct sockaddr_in client_addr;
int client_len = sizeof(client_addr);
//接收客户端,返回客户端套接字
int client = accept(server,(struct sockaddr*)&client_addr,&client_len);
if(client==-1)
{
ERRLOG("客户端接受失败");
}
printf("有客户端连接成功! ip=%s port=%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
if(client>fd_count)
fd_count=client;
//监视新客户端套接字,加入监视列表中
FD_SET(client,&list);
//将套接字加入数组中
insert_client(client_arr,&client_arr_len,client);
}
if(FD_ISSET(0,&backup))
{
char buf[128]="";
scanf("%s",buf);
getchar();
printf("键盘输入数据:%s\n",buf);
}
//某客户端激活,调用该客户端套接字读,遍历
for(int i=0;i<client_arr_len;i++)
{
int client=client_arr[i].fd;
if(FD_ISSET(client,&backup))
{
struct Client client_t;
int res=read(client,&client_t,sizeof(struct Client));
if(res==0)
{
printf("客户端断开连接!\n");
FD_CLR(client,&list); //监视列表移除该客户端
rm_client(client_arr,&client_arr_len,client); //数组中删除该客户端
continue;
}
//转发消息
printf("客户端发来消息:%s\n",client_t.mtext);
for(int i=0;i<client_arr_len;i++)
{
if(client_t.id == client_arr[i].id && client_arr[i].fd != client)
{
write(client_arr[i].fd,&client_t,sizeof(struct Client));
continue;
}
}
}
}
}
close(server);
return 0;
}
客户端代码
#include <25041head.h>
struct Client
{
int fd; //发送者id端口号
int id; //接收者端口号
char mtext[1024]; //发送的消息内容
};
int main(int argc, const char *argv[])
{
if(argc<2)
{
printf("请输入端口号!\n");
return 1;
}
short port=atoi(argv[1]);
int client=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in addr={0};
addr.sin_family=AF_INET;
addr.sin_port=htons(port); //端口写服务器写的端口号
addr.sin_addr.s_addr=inet_addr("127.0.0.1");
int a=connect(client,(struct sockaddr*)&addr,sizeof(addr));
if(a==-1)
{
printf("连接失败\n");
return 0;
}
printf("链接成功\n");
struct pollfd fds[2];
// 初始化第一个元素(标准输入)
fds[0].fd = 0;
fds[0].events = POLLIN | POLLOUT;
fds[0].revents = 0;
// 初始化第二个元素(客户端套接字)
fds[1].fd = client;
fds[1].events = POLLIN;
fds[1].revents = 0;
while(1)
{
poll(fds,2,-1);
if(fds[0].revents & POLLOUT)
{
printf("请输入接收消息客户端的id:");
fflush(stdout);
fds[0].events=POLLIN;
continue;
}
if(fds[0].revents & POLLIN)
{
int id_t;
char mtext_t[128]="";
scanf("%d",&id_t);
printf("请输入发送的消息内容:");
scanf("%s",mtext_t);
getchar();
struct Client client_msg={.fd=client,.id=id_t};
strcpy(client_msg.mtext,mtext_t);
write(client,&client_msg,sizeof(struct Client));
fds[0].events=POLLIN|POLLOUT;
}
if(fds[1].revents & POLLIN) // 接收消息
{
struct Client rev_msg;
int res=read(client,&rev_msg,sizeof(struct Client));
if(res==0)
{
printf("客户端已断开\n");
break;
}
printf("接收到来自客户端id为:%d 的消息:%s\n",rev_msg.id,rev_msg.mtext);
continue;
}
}
close(client);
return 0;
}