当前位置: 代码迷 >> 综合 >> 基于UDP的聊天系统 Ubuntu14.04
  详细解决方案

基于UDP的聊天系统 Ubuntu14.04

热度:19   发布时间:2023-11-22 03:18:08.0

总体思想:
先是创建一个链表用来存放连入服务器的sockaddr_in信息,有人登录加入链表节点,有人下线删除节点。
创建一个类似消息队列结构的结构体,用来存放用户的当前状态的类型、名字、消息。
将每个UDP客户端连接服务器后将其的sockaddr_in信息加入到链表中,通过数据的类型去判断服务器去执行哪个函数(登录广播、转发聊天信息、下线广播)。
1、UDP服务器部分
第一点 运行服务器会fork()一个子进程,该子进程用于服务器本身发送聊天的内容,父进程用于接收客户端的消息。
第二点 服务器通过接收的信息去判断类型(‘L’ ‘C’ ‘Q’)执行相应的处理函数。
第三点 处理函数 type = ‘L’ 会向当前链表中除自己以外的所有成员广播 name上线,随后将其加入到链表中。type = ‘C’ 会向当前链表中除自己以外的所有成员广播消息内容。type = ‘Q’ 首先是通知所有用户name下线,在链表中将其删除。
2、客户端部分
第一点 运行客户端程序,首先是将消息类型设置成’L’,令服务器广播告知其他成员该用户上线。
第二点 随后创建一个子进程,该进程用于发送聊天的消息,父进程用于接收服务器转发的其他用户发过来的消息。
第三点 当子进程判断输入的消息是"quit"将数据类型改成’Q’,以便服务器将其在链表中删除,并告知其他用户某人下线。
service:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>#define  N  128 typedef struct msg
{char type;char name[N];char text[N];
}msg_t;int main(int argc, const char *argv[])
{char buf[N] = {0};int sockfd,ret,newsockfd;struct sockaddr_in server,client;pid_t pid;msg_t msg;memset(&msg,0,sizeof(msg)) ;if(argc != 3){printf("运行程序时带入ip 和 port\n");exit(-1);}socklen_t addrlen = sizeof(client);sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){perror("socket");exit(-1);}printf("sockfd=%d\n",sockfd);memset(&server,0,sizeof(server));server.sin_family = AF_INET ;server.sin_port   = htons( atoi(argv[2]) );server.sin_addr.s_addr   = inet_addr(argv[1]); msg.type = 'L';printf("input your name >:");fgets(msg.name,N,stdin);msg.name[strlen(msg.name) -1 ] = 0 ; ret  = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ;if(ret < 0){perror("write");exit(-1);}pid = fork();if(pid < 0){perror("fork");exit(-1);}else if(pid == 0){msg.type = 'C';while(1){printf(">:");fgets(msg.text,N,stdin);if(strncmp(msg.text,"quit",4) == 0 ){msg.type = 'Q';ret  = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ;if(ret < 0){perror("write");exit(-1);}kill(getppid(),SIGUSR1);exit(0);}ret  = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ;if(ret < 0){perror("write");exit(-1);}}}else {while(1){ret = recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL);if(ret  < 0){printf("read");exit(-1);}printf("%s\n",msg.text);}}close(sockfd);return 0;
}

client:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define  N  128typedef struct node 
{struct sockaddr_in  addr; //ip 地址struct node * next;
}linklist_t;   //链表typedef struct msg
{char type;char name[N];char text[N];
}msg_t;linklist_t * create_empty_linkist(void)
{linklist_t * h = (linklist_t *)malloc(sizeof(linklist_t)) ;h->next = NULL;memset(&h->addr,0,sizeof(h->addr));return h;
}int process_login(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp)
{linklist_t * p = h->next; int ret ;sprintf(msgp->text,"%s login",msgp->name);while(p != NULL){if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 ){ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr));if(ret  < 0){printf("sendto");exit(-1);}}p = p->next;}p = (linklist_t * )malloc(sizeof(linklist_t)) ; //倒叙插入法p->next = h->next;h->next = p ; p->addr = *addr;return 0;}
int process_chat(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp)
{linklist_t * p = h->next; char buf[128] = {0};int ret ;sprintf(buf,"%s said %s",msgp->name,msgp->text);strcpy(msgp->text,buf);while(p != NULL){if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 ){ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr));if(ret  < 0){printf("sendto");exit(-1);}}p = p->next;}return 0;}
int process_quit(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp)
{linklist_t * p = h->next; linklist_t * q;char buf[128] = {0};int ret ;sprintf(msgp->text,"%s offline",msgp->name);while(p != NULL){if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 ){ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr));if(ret  < 0){printf("sendto");exit(-1);}}p = p->next;}p = h ; while(p->next != NULL){if(memcmp(&p->next->addr,addr,sizeof(*addr)) == 0 ){q = p->next;p->next = p->next->next;free(q);break ;}p = p->next;}return 0;}int main(int argc, const char *argv[])
{char buf[N] ={0};int sockfd,ret,newsockfd;struct sockaddr_in myaddr,client;pid_t pid;msg_t msg;memset(&msg,0,sizeof(msg));if(argc != 3){printf("运行程序时带入ip 和 port\n");exit(-1);}socklen_t addrlen = sizeof(client);sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){perror("socket");exit(-1);}printf("sockfd=%d\n",sockfd);memset(&myaddr,0,sizeof(myaddr));myaddr.sin_family = AF_INET ;myaddr.sin_port   = htons(atoi(argv[2]));myaddr.sin_addr.s_addr   = inet_addr(argv[1]); ret = bind(sockfd,(struct sockaddr *)&myaddr,sizeof(myaddr)) ; // 给socket 一个固定的ip 和端口 if(ret < 0){perror("bind");exit(-1);}pid = fork();if(pid < 0){perror("fork");exit(-1);}else if(pid == 0 ) // 发送广播{msg.type = 'C';while(1){printf(">:");fgets(msg.text,N,stdin);strcpy(msg.name,"server");ret  = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&myaddr,addrlen) ;if(ret < 0){perror("write");exit(-1);}memset(buf,0,N);}}else // parent {linklist_t * H = create_empty_linkist();while(1){ret = recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&client,&addrlen);if(ret  < 0){printf("read");exit(-1);}printf("msg.type = %c\n",msg.type);printf("msg.name = %s\n",msg.name);printf("msg.text = %s\n",msg.text);switch(msg.type){case 'L':process_login(H,sockfd,&client,&msg);break ; case 'C': process_chat(H,sockfd,&client,&msg);break ;case 'Q':process_quit(H,sockfd,&client,&msg);break ; default:break ;}
#if 0ret = sendto(sockfd,buf,N,0,(struct sockaddr *)&client,addrlen);if(ret  < 0){printf("sendto");exit(-1);}
#endif}}return 0;
}