UDP聊天系统
服务器端
#include <myhead.h>
#define SER_IP "192.168.2.8"
#define SER_PORT 8888typedef 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 8888int 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;
}