前言
基于UDP的网络聊天室
一、项目需求
- 如果有用户登录,其他用户可以收到这个人的登录信息
- 如果有人发送信息,其他用户可以收到这个人的群聊信息
- 如果有人下线,其他用户可以收到这个人的下线信息
- 服务器可以发送系统信息
二、步骤
1.创建UDP服务器
1.1 头文件
cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/select.h>
#include <poll.h>
#include <sqlite3.h>
1.2 定义宏
cpp
#define BUFSIZE 1024//定义缓冲区大小
#define login_msg_len 20//定义登录消息长度
1.3 定义链表节点 数据传递消息类型
cpp
/*定义链表节点*/
typedef struct Node{
char name[login_msg_len];
struct sockaddr_in addr;
struct Node *next;
}Node;
/*定义消息类型*/
typedef struct Msgtype{
char type;
char username[login_msg_len];
char content[BUFSIZE];
}Msgtype;
1.4 声明函数
cpp
Node *create_node(in_port_t SER_PORT,const char * SER_IP_STR);/*创建单向链表*/
Node *add_node(char *name,struct sockaddr_in addr);/*向链表中添加节点*/
int print_msg(Msgtype *msg);/*打印消息类型*/
void broadcast(int sfd,Node *head,Msgtype *msg);/*广播消息*/
int is_exist(Node *head,Msgtype *msg);/*查找用户名是否存在*/
int is_empty(Node *head);/*链表判空*/
void insert_node(Node *head,char *name,struct sockaddr_in addr);/*头插法插入节点*/
void delete_node(Node *head,char *name);/*按用户名删除节点*/
void broadcast_system(int sfd,Node *head,char *input_buf);/*向所有客户端发送系统消息*/
1.5 创建单向链表
cpp
Node *create_node(in_port_t SER_PORT,const char * SER_IP_STR){
Node *node=(Node*)malloc(sizeof(Node));
if(node==NULL){
perror("malloc");
exit(1);
}
char *msg="system";
for(int i=0;i<sizeof(msg);i++){
node->name[i]=msg[i];
}
node->addr.sin_family=AF_INET;
node->addr.sin_port=htons(SER_PORT);
node->addr.sin_addr.s_addr=inet_addr(SER_IP_STR);
node->next=NULL;
return node;
}
1.6 向链表中加入节点
cpp
Node *add_node(char *name,struct sockaddr_in addr){
Node *new_node=(Node*)malloc(sizeof(Node));
if(new_node==NULL){
perror("malloc");
exit(1);
}
strcpy(new_node->name,name);
new_node->addr=addr;
new_node->next=NULL;
return new_node;
}
1.7 对链表判空
cpp
int is_empty(Node *head){
if(head==NULL){
return 1;
}
return head->next==NULL?1:0;
}
1.8 插入节点
cpp
oid insert_node(Node *head,char *name,struct sockaddr_in addr){
if(head==NULL){
perror("head is null");
exit(1);
}
Node *new_node=add_node(name,addr);
new_node->next=head->next;
head->next=new_node;
printf("%s进入聊天室\n",name);
}
1.9 判断数据模式
cpp
int print_msg(Msgtype *msg){
switch(msg->type){
case 'L':
return 1;
case 'C':
return 2;
case 'Q':
return 3;
default:
return 0;
}
}
2.0 查找用户是否存在
cpp
int is_exist(Node *head,Msgtype *msg){
if(head==NULL){
perror("head is null");
exit(1);
}
if(is_empty(head)){
return 0;
}
Node *p=head->next;
while(p!=NULL){
if(strcmp(p->name,msg->username)==0){
return 1;
}
p=p->next;
}
return 0;
}
2.1 广播消息
cpp
void broadcast(int sfd,Node *head,Msgtype *msg){
if(head==NULL){
perror("head is null");
exit(1);
}
if(is_empty(head)){
printf("聊天室为空\n");
return;
}
Node *p=head->next;
while(p!=NULL){
/*发送消息*/
if(strcmp(p->name,msg->username)!=0){
sendto(sfd,msg,sizeof(Msgtype),0,(struct sockaddr*)&p->addr,sizeof(p->addr));
}
p=p->next;
}
}
2.2 按用户名删除用户节点
cpp
void delete_node(Node *head,char *name){
if(head==NULL){
perror("head is null");
exit(1);
}
if(is_empty(head)){
printf("聊天室为空\n");
return;
}
printf("%s退出聊天室\n",name);
Node *p=head->next;
Node *pre=head;
while(p!=NULL){
if(strcmp(p->name,name)==0){
pre->next=p->next;
free(p);
return;
}
pre=p;
p=p->next;
}
2.3 广播系统信息
cpp
void broadcast_system(int sfd,Node *head,char *content){
if(head==NULL){
perror("head is null");
exit(1);
}
if(is_empty(head)){
printf("聊天室为空\n");
return;
}
Msgtype msg;
msg.type='C';
strcpy(msg.username,"system");
strcpy(msg.content,content);
Node *p=head->next;
while(p!=NULL){
/*发送消息*/
sendto(sfd,&msg,sizeof(Msgtype),0,(struct sockaddr*)&p->addr,sizeof(p->addr));
p=p->next;
}
printf("system发送消息:%s\n",content);
}
2.4 创建套接字,绑定IP,端口信息
cpp
/*获取IP地址和端口*/
if(3>argc){
printf("输入格式不对,请按照以下格式输入:./server ip port\n");
exit(1);
}
const char *SER_IP_STR=argv[1];
in_port_t SER_PORT=atoi(argv[2]);
/*创建UDP套接字*/
int sfd=-1;
if((sfd=socket(AF_INET,SOCK_DGRAM,0))<0){
perror("socket");
exit(1);
}
/*设置UDP服务器的地址*/
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(SER_PORT);
if(inet_aton(SER_IP_STR,&addr.sin_addr)==0){
perror("inet_aton");
exit(1);
}
/*绑定端口*/
if(bind(sfd,(struct sockaddr*)&addr,sizeof(addr))<0){
perror("bind");
exit(1);
}
printf("bind success\n");
2.5 创建链表
cpp
/*创建链表*/
Node *head=create_node(SER_PORT,SER_IP_STR);
2.6 使用poll实现并发执行
cpp
/*监控服务器响应*/
struct pollfd pfd[2];
pfd[0].fd = sfd; // 监控套接字
pfd[0].events = POLLIN; // 监控读事件
pfd[1].fd = 0; // 监控标准输入
pfd[1].events = POLLIN; // 监控读事件
cpp
/*等待IO事件*/
int ret=poll(pfd,2,-1);
if(ret<0){
perror("poll");
exit(1);
}else if (ret==0){
printf("poll timeout\n");
continue;
}
cpp
if(pfd[0].revents==POLLIN){
}
if(pfd[1].revents==POLLIN){
}
2.7 创建接收客户端网络信息,数据信息的结构体
cpp
/*创建接收客户端网络信息的结构体*/
struct sockaddr_in client_addr;
socklen_t client_addr_len=sizeof(client_addr);
/*创建客户端数据信息的结构体*/
Msgtype client_msg;
memset(&client_msg,0,sizeof(client_msg));
2.8 接收数据处理
cpp
int recv_len=recvfrom(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,&client_addr_len);
if(recv_len<0){
perror("recvfrom");
exit(1);
}
/*判断消息类型*/
/*登录类型*/
if(print_msg(&client_msg)==1){
/*判断用户是否存在*/
if(is_exist(head,&client_msg)){
/*向客户端发送用户名已存在消息*/
client_msg.type='E';
sendto(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,sizeof(client_addr));
continue;
}else{
/*向链表中插入节点*/
insert_node(head,client_msg.username,client_addr);
/*向客户端发送登录成功消息*/
client_msg.type='L';
strcpy(client_msg.content,"进入聊天室");
/*向所有客户端发送登录消息*/
broadcast(sfd,head,&client_msg);
}
}else if (print_msg(&client_msg)==2){
/*客户端数据传输类型*/
/*向所有客户端发送消息*/
printf("%s发送消息\n",client_msg.username);
broadcast(sfd,head,&client_msg);
}else if (print_msg(&client_msg)==3){
/*客户端退出登录类型*/
/*删除节点*/
delete_node(head,client_msg.username);
/*向客户端发送退出登录消息*/
client_msg.type='Q';
strcpy(client_msg.content,"退出聊天室");
broadcast(sfd,head,&client_msg);
}else{
/*客户端消息类型错误*/
char *msg="消息类型错误";
for(int i=0;i<sizeof(msg);i++){
client_msg.content[i]=msg[i];
}
sendto(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,sizeof(client_msg));
continue;
}
2.9 发送数据处理
cpp
/*服务器终端输入*/
char input_buf[BUFSIZE];
fgets(input_buf,BUFSIZE,stdin);
input_buf[strlen(input_buf)-1]='\0';
/*判断客户端是否登录*/
if(is_empty(head)){
printf("聊天室为空,请等待其他用户登录\n");
continue;
}
/*向所有客户端发送消息*/
broadcast_system(sfd,head,input_buf);
2.创建UDP客户端
1.1 定义宏
cpp
#define BUFSIZE 1024//定义缓冲区大小
#define login_msg_len 20//定义登录信息长度
1.2 定义数据信息结构体
cpp
typedef struct Msgtype{
char type;
char username[login_msg_len];
char content[BUFSIZE];
}Msgtype;
1.3 创建套接字,绑定服务器
cpp
/*获取IP地址和端口*/
if(3>argc){
printf("输入格式不对,请按照以下格式输入:./server ip port\n");
}
const char *SER_IP_STR=argv[1];
int SER_PORT=atoi(argv[2]);
/*创建UDP套接字*/
int sfd=-1;
if((sfd=socket(AF_INET,SOCK_DGRAM,0))<0){
perror("socket");
exit(1);
}
/*设置服务器地址*/
struct sockaddr_in server_addr;
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(SER_PORT);
if(inet_aton(SER_IP_STR,&server_addr.sin_addr)==0){
perror("inet_aton");
exit(1);
}
socklen_t addr_len=sizeof(server_addr);
1.4 创建客户端数据手收发结构体,用户登录信息结构体
cpp
/*创建客户端数据收发结构体*/
Msgtype recv_msg;
memset(&recv_msg,0,sizeof(recv_msg));
Msgtype send_msg;
memset(&send_msg,0,sizeof(send_msg));
/*创建用户登录信息*/
Msgtype login_msg;
1.5 用户登录
cpp
/*登录服务器*/
memset(&login_msg,0,sizeof(login_msg));
login_msg.type='L';
printf("请输入用户名:");
fgets(login_msg.username, login_msg_len, stdin);
login_msg.username[strcspn(login_msg.username, "\n")] = '\0'; // 去掉换行符
/*发送登录信息*/
if (sendto(sfd, &login_msg, sizeof(login_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("sendto");
exit(1);
}
1.6 使用poll实现并发
cpp
/*监控服务器响应*/
struct pollfd pfd[2];
pfd[0].fd = sfd; // 监控套接字
pfd[0].events = POLLIN; // 监控读事件
pfd[1].fd = 0; // 监控标准输入
pfd[1].events = POLLIN; // 监控读事件;
cpp
/*等待IO事件*/
int ret=poll(pfd,2,-1);
if(ret<0){
perror("poll");
exit(1);
}else if (ret==0)
{
printf("poll timeout\n");
continue;
}
cpp
if(pfd[0].revents==POLLIN){
}
if(pfd[1].revents==POLLIN){
}
1.7 根据条件进行并发执行终端输入,数据接收
cpp
if(pfd[0].revents==POLLIN){
/*接收服务器消息*/
int len=recvfrom(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr *)&server_addr, &addr_len);
if(len<0){
perror("recvfrom");
exit(1);
}
if(recv_msg.type=='L'){
printf("-----------%s加入聊天室----------\n",recv_msg.username);
}else if(recv_msg.type=='C'){
printf("%s:%s\n",recv_msg.username,recv_msg.content);
}else if (recv_msg.type=='Q') {
printf("------------%s%s---------------\n",recv_msg.username,recv_msg.content);
}else{
printf("%s用户名已存在或服务器故障,请重新登录\n",login_msg.username);
break;
}
}
if(pfd[1].revents==POLLIN){
/*接收用户输入*/
char input[BUFSIZE];
fgets(input,BUFSIZE, stdin);
input[strcspn(input, "\n")] = '\0'; // 去掉换行符
if(strcmp(input,"quit")==0){
send_msg.type='Q';
sendto(sfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
break;//退出循环
}else{
/*发送消息*/
memset(&send_msg,0,sizeof(send_msg));
send_msg.type='C';
strncpy(send_msg.username,login_msg.username,login_msg_len);
strncpy(send_msg.content,input,BUFSIZE);
sendto(sfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
}
}
3.服务器最终程序
cpp
#include <myhead.h>
#define BUFSIZE 1024//定义缓冲区大小
#define login_msg_len 20//定义登录消息长度
/*定义链表节点*/
typedef struct Node{
char name[login_msg_len];
struct sockaddr_in addr;
struct Node *next;
}Node;
/*定义消息类型*/
typedef struct Msgtype{
char type;
char username[login_msg_len];
char content[BUFSIZE];
}Msgtype;
/*声明函数*/
Node *create_node(in_port_t SER_PORT,const char * SER_IP_STR);/*创建单向链表*/
Node *add_node(char *name,struct sockaddr_in addr);/*向链表中添加节点*/
int print_msg(Msgtype *msg);/*打印消息类型*/
void broadcast(int sfd,Node *head,Msgtype *msg);/*广播消息*/
int is_exist(Node *head,Msgtype *msg);/*查找用户名是否存在*/
int is_empty(Node *head);/*链表判空*/
void insert_node(Node *head,char *name,struct sockaddr_in addr);/*头插法插入节点*/
void delete_node(Node *head,char *name);/*按用户名删除节点*/
void broadcast_system(int sfd,Node *head,char *input_buf);/*向所有客户端发送系统消息*/
int main(int argc, char const *argv[])
{
/*获取IP地址和端口*/
if(3>argc){
printf("输入格式不对,请按照以下格式输入:./server ip port\n");
exit(1);
}
const char *SER_IP_STR=argv[1];
in_port_t SER_PORT=atoi(argv[2]);
/*创建UDP套接字*/
int sfd=-1;
if((sfd=socket(AF_INET,SOCK_DGRAM,0))<0){
perror("socket");
exit(1);
}
/*设置UDP服务器的地址*/
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(SER_PORT);
if(inet_aton(SER_IP_STR,&addr.sin_addr)==0){
perror("inet_aton");
exit(1);
}
/*绑定端口*/
if(bind(sfd,(struct sockaddr*)&addr,sizeof(addr))<0){
perror("bind");
exit(1);
}
printf("bind success\n");
/*创建链表*/
Node *head=create_node(SER_PORT,SER_IP_STR);
/*监控服务器响应*/
struct pollfd pfd[2];
pfd[0].fd = sfd; // 监控套接字
pfd[0].events = POLLIN; // 监控读事件
pfd[1].fd = 0; // 监控标准输入
pfd[1].events = POLLIN; // 监控读事件
/*创建接收客户端网络信息的结构体*/
struct sockaddr_in client_addr;
socklen_t client_addr_len=sizeof(client_addr);
/*创建客户端数据信息的结构体*/
Msgtype client_msg;
memset(&client_msg,0,sizeof(client_msg));
/*循环接收客户端信息*/
while(1){
/*等待IO事件*/
int ret=poll(pfd,2,-1);
if(ret<0){
perror("poll");
exit(1);
}else if (ret==0){
printf("poll timeout\n");
continue;
}
if(pfd[0].revents==POLLIN){
/*接收客户端登录信息*/
int recv_len=recvfrom(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,&client_addr_len);
if(recv_len<0){
perror("recvfrom");
exit(1);
}
/*判断消息类型*/
/*登录类型*/
if(print_msg(&client_msg)==1){
/*判断用户是否存在*/
if(is_exist(head,&client_msg)){
/*向客户端发送用户名已存在消息*/
client_msg.type='E';
sendto(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,sizeof(client_addr));
continue;
}else{
/*向链表中插入节点*/
insert_node(head,client_msg.username,client_addr);
/*向客户端发送登录成功消息*/
client_msg.type='L';
strcpy(client_msg.content,"进入聊天室");
/*向所有客户端发送登录消息*/
broadcast(sfd,head,&client_msg);
}
}else if (print_msg(&client_msg)==2){
/*客户端数据传输类型*/
/*向所有客户端发送消息*/
printf("%s发送消息\n",client_msg.username);
broadcast(sfd,head,&client_msg);
}else if (print_msg(&client_msg)==3){
/*客户端退出登录类型*/
/*删除节点*/
delete_node(head,client_msg.username);
/*向客户端发送退出登录消息*/
client_msg.type='Q';
strcpy(client_msg.content,"退出聊天室");
broadcast(sfd,head,&client_msg);
}else{
/*客户端消息类型错误*/
char *msg="消息类型错误";
for(int i=0;i<sizeof(msg);i++){
client_msg.content[i]=msg[i];
}
sendto(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,sizeof(client_msg));
continue;
}
}
if(pfd[1].revents==POLLIN){
/*服务器终端输入*/
char input_buf[BUFSIZE];
fgets(input_buf,BUFSIZE,stdin);
input_buf[strlen(input_buf)-1]='\0';
/*判断客户端是否登录*/
if(is_empty(head)){
printf("聊天室为空,请等待其他用户登录\n");
continue;
}
/*向所有客户端发送消息*/
broadcast_system(sfd,head,input_buf);
}
}
/*关闭套接字*/
close(sfd);
return 0;
}
/*创建单向链表*/
Node *create_node(in_port_t SER_PORT,const char * SER_IP_STR){
Node *node=(Node*)malloc(sizeof(Node));
if(node==NULL){
perror("malloc");
exit(1);
}
char *msg="system";
for(int i=0;i<sizeof(msg);i++){
node->name[i]=msg[i];
}
node->addr.sin_family=AF_INET;
node->addr.sin_port=htons(SER_PORT);
node->addr.sin_addr.s_addr=inet_addr(SER_IP_STR);
node->next=NULL;
return node;
}
/*向链表中添加节点*/
Node *add_node(char *name,struct sockaddr_in addr){
Node *new_node=(Node*)malloc(sizeof(Node));
if(new_node==NULL){
perror("malloc");
exit(1);
}
strcpy(new_node->name,name);
new_node->addr=addr;
new_node->next=NULL;
return new_node;
}
/*链表判空*/
int is_empty(Node *head){
if(head==NULL){
return 1;
}
return head->next==NULL?1:0;
}
/*头插法插入节点*/
void insert_node(Node *head,char *name,struct sockaddr_in addr){
if(head==NULL){
perror("head is null");
exit(1);
}
Node *new_node=add_node(name,addr);
new_node->next=head->next;
head->next=new_node;
printf("%s进入聊天室\n",name);
}
/*判断数据模式*/
int print_msg(Msgtype *msg){
switch(msg->type){
case 'L':
return 1;
case 'C':
return 2;
case 'Q':
return 3;
default:
return 0;
}
}
/*查找用户名是否存在*/
int is_exist(Node *head,Msgtype *msg){
if(head==NULL){
perror("head is null");
exit(1);
}
if(is_empty(head)){
return 0;
}
Node *p=head->next;
while(p!=NULL){
if(strcmp(p->name,msg->username)==0){
return 1;
}
p=p->next;
}
return 0;
}
/*广播消息*/
void broadcast(int sfd,Node *head,Msgtype *msg){
if(head==NULL){
perror("head is null");
exit(1);
}
if(is_empty(head)){
printf("聊天室为空\n");
return;
}
Node *p=head->next;
while(p!=NULL){
/*发送消息*/
if(strcmp(p->name,msg->username)!=0){
sendto(sfd,msg,sizeof(Msgtype),0,(struct sockaddr*)&p->addr,sizeof(p->addr));
}
p=p->next;
}
}
/*按用户名删除节点*/
void delete_node(Node *head,char *name){
if(head==NULL){
perror("head is null");
exit(1);
}
if(is_empty(head)){
printf("聊天室为空\n");
return;
}
printf("%s退出聊天室\n",name);
Node *p=head->next;
Node *pre=head;
while(p!=NULL){
if(strcmp(p->name,name)==0){
pre->next=p->next;
free(p);
return;
}
pre=p;
p=p->next;
}
}
/*广播system消息*/
void broadcast_system(int sfd,Node *head,char *content){
if(head==NULL){
perror("head is null");
exit(1);
}
if(is_empty(head)){
printf("聊天室为空\n");
return;
}
Msgtype msg;
msg.type='C';
strcpy(msg.username,"system");
strcpy(msg.content,content);
Node *p=head->next;
while(p!=NULL){
/*发送消息*/
sendto(sfd,&msg,sizeof(Msgtype),0,(struct sockaddr*)&p->addr,sizeof(p->addr));
p=p->next;
}
printf("system发送消息:%s\n",content);
}
4.客户端最终程序
cpp
#include <myhead.h>
#define BUFSIZE 1024//定义缓冲区大小
#define login_msg_len 20//定义登录信息长度
/*定义消息类型*/
typedef struct Msgtype{
char type;
char username[login_msg_len];
char content[BUFSIZE];
}Msgtype;
int main(int argc, char const *argv[])
{
/*获取IP地址和端口*/
if(3>argc){
printf("输入格式不对,请按照以下格式输入:./server ip port\n");
}
const char *SER_IP_STR=argv[1];
int SER_PORT=atoi(argv[2]);
/*创建UDP套接字*/
int sfd=-1;
if((sfd=socket(AF_INET,SOCK_DGRAM,0))<0){
perror("socket");
exit(1);
}
/*设置服务器地址*/
struct sockaddr_in server_addr;
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(SER_PORT);
if(inet_aton(SER_IP_STR,&server_addr.sin_addr)==0){
perror("inet_aton");
exit(1);
}
socklen_t addr_len=sizeof(server_addr);
/*创建客户端数据收发结构体*/
Msgtype recv_msg;
memset(&recv_msg,0,sizeof(recv_msg));
Msgtype send_msg;
memset(&send_msg,0,sizeof(send_msg));
/*创建用户登录信息*/
Msgtype login_msg;
while (1)
{
/*登录服务器*/
memset(&login_msg,0,sizeof(login_msg));
login_msg.type='L';
printf("请输入用户名:");
fgets(login_msg.username, login_msg_len, stdin);
login_msg.username[strcspn(login_msg.username, "\n")] = '\0'; // 去掉换行符
/*发送登录信息*/
if (sendto(sfd, &login_msg, sizeof(login_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("sendto");
exit(1);
}
/*监控服务器响应*/
struct pollfd pfd[2];
pfd[0].fd = sfd; // 监控套接字
pfd[0].events = POLLIN; // 监控读事件
pfd[1].fd = 0; // 监控标准输入
pfd[1].events = POLLIN; // 监控读事件;
while(1){
/*等待IO事件*/
int ret=poll(pfd,2,-1);
if(ret<0){
perror("poll");
exit(1);
}else if (ret==0)
{
printf("poll timeout\n");
continue;
}
if(pfd[0].revents==POLLIN){
/*接收服务器消息*/
int len=recvfrom(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr *)&server_addr, &addr_len);
if(len<0){
perror("recvfrom");
exit(1);
}
if(recv_msg.type=='L'){
printf("-----------%s加入聊天室----------\n",recv_msg.username);
}else if(recv_msg.type=='C'){
printf("%s:%s\n",recv_msg.username,recv_msg.content);
}else if (recv_msg.type=='Q') {
printf("------------%s%s---------------\n",recv_msg.username,recv_msg.content);
}else{
printf("%s用户名已存在或服务器故障,请重新登录\n",login_msg.username);
break;
}
}
if(pfd[1].revents==POLLIN){
/*接收用户输入*/
char input[BUFSIZE];
fgets(input,BUFSIZE, stdin);
input[strcspn(input, "\n")] = '\0'; // 去掉换行符
if(strcmp(input,"quit")==0){
send_msg.type='Q';
sendto(sfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
break;//退出循环
}else{
/*发送消息*/
memset(&send_msg,0,sizeof(send_msg));
send_msg.type='C';
strncpy(send_msg.username,login_msg.username,login_msg_len);
strncpy(send_msg.content,input,BUFSIZE);
sendto(sfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
}
}
}
}
close(sfd);
return 0;
}