UDP聊天系统
服务器端
#include <myhead.h>
#define SER_IP "192.168.2.8"
#define SER_PORT 8888
typedef struct Node{
char username[20];
struct sockaddr_in cin;
struct Node *next;
}node,*node_p;
//创建头结点
node_p create_head()
{
node_p H=(node_p)malloc(sizeof(node)); //申请空间
if(H==NULL)
{
printf("空间申请失败\n");
return NULL;
}
H->next=NULL;
return H;
}
//新建结点
node_p create_node(char *username,struct sockaddr_in cin)
{
node_p new=(node_p)malloc(sizeof(node));
if(new==NULL)
{
printf("空间申请失败\n");
return NULL;
}
strcpy(new->username,username);
new->cin=cin;
return new;
}
int empty_ch(node_p H)
{
//入参判空
if(H==NULL)
{
printf("入参为空\n");
return -1;
}
return H->next==NULL?1:0;
}
//尾插
void insert_tail(node_p H,char *username,struct sockaddr_in cin)
{
//判空
if(H==NULL)
{
printf("入参为空\n");
return;
}
node_p p=H;
//循环到链表尾
while(p->next != NULL)
p=p->next;
node_p new=create_node(username,cin);
new->next=p->next;
p->next=new;
}
//查找指定的客户端结点
char *find(node_p H,struct sockaddr_in cin)
{
if(H==NULL)
{
printf("入参为空\n");
return NULL;
}
if(H->next==NULL)
{
printf("链表为空\n");
return NULL;
}
//循环,找出IP、结点相同的结点名称
node_p p=H->next;
while(p != NULL)
{
if((p->cin.sin_addr.s_addr == cin.sin_addr.s_addr) && (p->cin.sin_port == cin.sin_port))
return p->username;
p=p->next;
}
return NULL;
}
//删除指定的客户端信息结点
void delete_node(node_p H,struct sockaddr_in cin)
{
//判空
if(H==NULL)
{
printf("入参为空\n");
return;
}
if(H->next==NULL)
{
printf("链表为空\n");
return;
}
node_p p=H->next;
//循环找到指定结点的上一个结点
while((p->next->cin.sin_addr.s_addr != cin.sin_addr.s_addr) || (p->cin.sin_port != cin.sin_port))
p=p->next;
//保存要删除的结点
node_p del=p->next;
//将上一个结点指向下下个结点地址
p->next=p->next->next;
//释放要删除的结点空间
free(del);
}
int main(int argc, const char *argv[])
{
//创建UDP套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
printf("%d\n",sfd);
//绑定IP地址和端口
//初始化结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(SER_IP);
sin.sin_port = htons(SER_PORT);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//使用poll函数完成任务并发
struct pollfd pfd[2];
pfd[0].fd=0; //检查0号文件描述符
pfd[0].events=POLLIN; //检查读事件
pfd[1].fd=sfd; //检查sfd号文件描述符
pfd[1].events=POLLIN; //检查读事件
//创建头结点
node_p H=create_head();
//信息收发容器
char rbuf[128]="";
char wbuf[128]="";
//准备获取对端地址信息
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
while(1)
{
//poll函数,2个文件描述符,永久阻塞
poll(pfd,2,-1);
//判断是否为0的事件,服务器向所有客户端发送信息
if(pfd[0].revents == POLLIN)
{
bzero(wbuf,sizeof(wbuf));
fgets(wbuf,sizeof(wbuf),stdin);
wbuf[strlen(wbuf)-1]=0;
//格式化要发送的信息
char str[256]="";
snprintf(str,sizeof(str),"服务器:%s",wbuf);
//将信息发送给所有客户端
if(empty_ch(H) == 1)
{
printf("还未有用户加入聊天室\n");
return -1;
}
node_p p=H->next;
while(p != NULL)
{
if(sendto(sfd,str,strlen(str),0,(struct sockaddr*)&p->cin,sizeof(p->cin)) == -1)
{
perror("sendto error");
return -1;
}
p=p->next;
}
//记录服务器操作
printf("服务器[%s:%d]发送了一条消息\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port));
}
//判断是否为sfd的事件,客户端间通信
if(pfd[1].revents == POLLIN)
{
bzero(rbuf,sizeof(rbuf));
//接收客户端信息
if(recvfrom(sfd,rbuf,sizeof(rbuf),0,(struct sockaddr*)&cin,&addrlen) == -1)
{
perror("recvfrom error");
return -1;
}
//判断客户端地址是否已接入
char *ret=find(H,cin);
//用户首次接入
if(ret == NULL)
{
//插入用户名和地址信息
insert_tail(H,rbuf,cin);
//服务器端输出客户端加入的信息
printf("%s [%s:%d]加入了聊天室\n",rbuf,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
//将客户端加入的信息发送给所有客户端
char str[256]="";
snprintf(str,sizeof(str),"----%s加入了聊天室----",rbuf);
node_p p=H->next;
while(p->next != NULL)
{
if(sendto(sfd,str,strlen(str),0,(struct sockaddr*)&p->cin,sizeof(p->cin)) == -1)
{
perror("sendto error");
return -1;
}
p=p->next;
}
}
//用户先前已接入
else if(ret>0)
{
//用户退出请求
if(strcmp(rbuf,"quit") == 0)
{
//服务器端输出客户端退出的信息
printf("%s [%s:%d]退出了聊天室\n",rbuf,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
//将退出信息发送给服务器端和所有客户端
char str[256]="";
sprintf(str,"----%s退出了聊天室----",ret);
node_p p=H->next;
while(p != NULL)
{
if(sendto(sfd,str,strlen(str),0,(struct sockaddr*)&p->cin,sizeof(p->cin)) == -1)
{
perror("sendto error");
return -1;
}
p=p->next;
}
//删除链表中删除对应的结点
delete_node(H,cin);
}
//用户聊天信息
else
{
//将信息发送给其他用户
char str[256]="";
snprintf(str,sizeof(str),"%s:%s",ret,rbuf);
node_p p=H->next;
while(p != NULL)
{
//跳过自己
if((p->cin.sin_addr.s_addr == cin.sin_addr.s_addr) && (p->cin.sin_port == cin.sin_port))
{
p=p->next;
continue;
}
//将消息发送给其他用户
if(sendto(sfd,str,strlen(str),0,(struct sockaddr*)&p->cin,sizeof(p->cin)) == -1)
{
perror("sendto error");
return -1;
}
p=p->next;
}
//服务器端记录用户的操作
printf("%s [%s:%d]发送了一条消息\n",ret,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
}
}
}
}
close(sfd);
return 0;
}
客户端
#include <myhead.h>
#define SER_IP "192.168.2.8"
#define SER_PORT 8888
int main(int argc, const char *argv[])
{
//创建UDP套接字
int cfd = socket(AF_INET,SOCK_DGRAM,0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_addr.s_addr=inet_addr(SER_IP);
sin.sin_port=htons(SER_PORT);
struct pollfd pfd[2];
pfd[0].fd=0;
pfd[0].events=POLLIN;
pfd[1].fd=1;
pfd[1].events=POLLIN;
printf("请输入昵称:>>>>>");
fflush(stdout);
char wbuf[128]="";
char rbuf[256]="";
while(1)
{
bzero(wbuf,sizeof(wbuf));
bzero(rbuf,sizeof(rbuf));
poll(pfd,2,-1);
//客户端输入信息
if(pfd[0].revents == POLLIN)
{
fgets(wbuf,sizeof(wbuf),stdin);
wbuf[strlen(wbuf)-1]='\0';
//将信息发送给服务器
if(sendto(cfd,wbuf,strlen(wbuf),0,(struct sockaddr*)&sin,sizeof(sin)) == -1)
{
perror("sendto error");
return -1;
}
//客户端退出
if(strcmp(wbuf,"quit") == 0)
{
close(cfd);
return 0;
}
}
//客户端接收信息
if(pfd[1].revents == POLLIN)
{
if(recvfrom(cfd,rbuf,sizeof(rbuf),0,NULL,NULL) == -1)
{
perror("recvfrom error");
return -1;
}
printf("%s\n",rbuf);
}
}
close(cfd);
return 0;
}