linux下使用UDP实现简易的多人聊天室
来源:互联网 发布:数据挖掘 常用模型 编辑:程序博客网 时间:2024/06/02 19:55
本文采取的思想如下。
一、采用多进程处理方式。服务器端父进程负责接收处理客户端的消息并发送,子进程负责获取终端输入的内容并发送。客户端父进程负责接收服务器的消息并打印,子进程负责获取终端输入的内容并发送给服务器。
二、消息的分类,规划和整理。我们目前将消息划分为三个种类,登录消息,聊天消息,退出消息。每种消息对应不同的广播方式。一般来说单个客户端自己发出的消息不会显示在自己的终端,而其他客户端会接收到该客户端的操作记录消息。
三、采用链表存储每个客户端的网络信息,登录对应聊表插入(使用头插法),退出对应链表删除。广播对应链表的遍历。
以下是全部代码
服务器端
/** *@filename server.c *@data2017/5/2 *@authorhaibo *@briefUDP聊天室服务器端 */#include <stdio.h> //printf#include <arpa/inet.h> //inet_addr htons#include <sys/types.h>#include <sys/socket.h> //socket bind listen accept connect#include <netinet/in.h> //sockaddr_in#include <stdlib.h> //exit#include <unistd.h> //close#include <string.h> //strcat#include <unistd.h>//fork#define N 512#define errlog(errmsg) do{perror(errmsg);\ printf("%s-->%s-->%d\n", __FILE__, __func__, __LINE__);\ exit(1);\ }while(0) /** L:表示消息类型为登录 B:表示消息类型为广播 Q:表示消息类型为退出 */ typedef struct msg//定义消息结构体{char type;/**< 消息类型 */char name[32]; /**< 消息来源标识*/char text[N]; /**< 消息内容*/}MSG;typedef struct node//存储网络信息结构体的链表{struct sockaddr_in addr;struct node *next;}listnode,*linklist;void process_login(int sockfd,linklist H,MSG msg,struct sockaddr_in clientaddr);//登录void process_chat(int sockfd,linklist H,MSG msg,struct sockaddr_in clientaddr);//客户端发送消息void process_quit(int sockfd,linklist H,MSG msg,struct sockaddr_in clientaddr);//退出/** *@brief 创建一个链表 */linklist linklist_creat(){linklist H;H=(linklist)malloc(sizeof(listnode));H->next=NULL;return H;}int main(int argc, const char *argv[]){MSG msg;int sockfd;pid_t pid;struct sockaddr_in serveraddr, clientaddr;socklen_t addrlen = sizeof(struct sockaddr);char buf[N] = {0};if(argc < 3){printf("argument is too few\n");exit(1);}//第一步:创建一个套接字if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){errlog("fail to socket");}//第二步:填充服务器网络信息结构体//inet_addr:将点分十进制IP地址转化为网络识别的//htons:将主机字节序转化为网络字节序//atoi:将数字型字符串转化为整型数据serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(argv[1]);serveraddr.sin_port = htons(atoi(argv[2]));//第三步:将套接字与网络信息结构体绑定if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){errlog("fail to bind");}pid=fork();if(pid<0){errlog("fork error");}/**子进程输入**/if(0==pid){memset(&msg,0,sizeof(msg));strcpy(msg.name,"server");msg.type='B';while(1){fgets(msg.text, N, stdin);msg.text[strlen(msg.text)-1]='\0';sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&(serveraddr),addrlen);}}/**父进程接收发送**/else{linklist H=linklist_creat();while(1){if(recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(clientaddr),&addrlen) <= 0){errlog("recvfrom error");}switch(msg.type){case 'L':process_login(sockfd,H,msg,clientaddr);break;case 'B':process_chat(sockfd,H,msg,clientaddr);break;case 'Q':process_quit(sockfd,H,msg,clientaddr);break;}}}close(sockfd);return 0;}/** *@brief 登录信息 *@param sockfd套接字描述符,H存储网络信息的链表头节点,msg传输的消息,clientaddr服务端的网络信息 *@return 无 *@note 先发送再插入目的是不将登录信息发送给自身 */void process_login(int sockfd,linklist H,MSG msg,struct sockaddr_in clientaddr){linklist p=H->next;//sprintf(msg.text,"%s上线了",msg.name);strcpy(msg.text,msg.name);msg.text[strlen(msg.text)]='\0';strcat(msg.text," 上线了");//printf(msg.text);puts(msg.text);while(p!=NULL){ sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->addr),sizeof(p->addr)); // printf("send %s to port %d\n",msg.text,ntohs((p->addr).sin_port)); p=p->next;}p=(linklist)malloc(sizeof(listnode));//使用头插法插入网络信息结构体数据p->addr=clientaddr;p->next=H->next;H->next=p;printf("get client port = %d.\n",ntohs((p->addr).sin_port));}/** *@brief 登录信息 *@param 略 *@return 无 *@note 比较网络信息,不将广播信息发送给自己 */ void process_chat(int sockfd,linklist H,MSG msg,struct sockaddr_in clientaddr) {linklist p = H->next;char s[N]={0};sprintf(s,"%s说: %s",msg.name,msg.text);strcpy(msg.text,s);puts(msg.text);while(p)//遍历链表{if(memcmp(&clientaddr,&p->addr,sizeof(clientaddr))!=0){if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&(p->addr),sizeof(p->addr))<0)errlog("fail to sendto");}p=p->next;} } /** *@brief 退出信息 *@param 略 *@return 无 *@note 比较网络信息,删除存储即将退出的客户端的网络信息的节点 */ void process_quit(int sockfd,linklist H,MSG msg,struct sockaddr_in clientaddr) {linklist p = H;linklist q=NULL;sprintf(msg.text,"%s 下线了",msg.name);puts(msg.text);while(p->next){if(memcmp(&clientaddr,&p->next->addr,sizeof(clientaddr))==0){q=p->next;p->next=q->next;free(q);q=NULL;}else {sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&(p->next->addr),sizeof(p->next->addr));//退出信息发送给其余未推出的所有客户端成员p=p->next;}} }
客户端
/** *@filename client.c *@data2017/5/2 *@authorhaibo *@briefUDP聊天室客户端 */#if 1#include <stdio.h> #include <arpa/inet.h> //inet_addr htons#include <sys/types.h>#include <sys/socket.h> //socket bind listen accept connect#include <netinet/in.h> //sockaddr_in#include <stdlib.h> //exit#include <unistd.h> //close#include <string.h> //strcat#include <signal.h>#define N 128#define errlog(errmsg) do{perror(errmsg);\ printf("%s-->%s-->%d\n", __FILE__, __func__, __LINE__);\ exit(1);\ }while(0)#endiftypedef struct msg{char type;char name[32];char text[N];}MSG;typedef struct node{struct sockaddr_in addr;struct node *next;}listnode,*linklist;int main(int argc, const char *argv[]){MSG msg;int sockfd;struct sockaddr_in serveraddr;socklen_t addrlen = sizeof(struct sockaddr);pid_t pid;if(argc < 3){printf("argument is too few\n");exit(1);}/**第一步:创建一个套接字*/if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){errlog("fail to socket");}/**第二步:填充服务器网络信息结构体 *inet_addr:将点分十进制IP地址转化为网络识别的 *htons:将主机字节序转化为网络字节序 *atoi:将数字型字符串转化为整型数据 */serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(argv[1]);serveraddr.sin_port = htons(atoi(argv[2]));printf("输入用户名:");fgets(msg.name,sizeof(msg.name),stdin);msg.name[strlen(msg.name)-1]='\0';msg.text[0]='\0';msg.type= 'L';if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&serveraddr,addrlen)<0){errlog("fail to sendto");}pid=fork();if(pid==0)/**子进程从终端获取输入,并发送消息**/{while(1){fgets(msg.text,N,stdin);msg.text[strlen(msg.text)-1]='\0';if(strncmp(msg.text,"quit",4)==0){msg.type='Q';if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&serveraddr,addrlen)<0){errlog("fail to sendto");}printf("quit !");kill(getppid(),SIGKILL);exit(1);}else{msg.type='B';if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&serveraddr,addrlen)<0){errlog("fail to sendto");}}}}else/**父进程接收服务器端的消息并打印**/{while(1){recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, &addrlen); puts(msg.text);}}close(sockfd);return 0;}
1 0
- linux下使用UDP实现简易的多人聊天室
- linux下 UDP 实现聊天室
- C 基于UDP实现一个简易的聊天室
- 网络编程初探--使用UDP协议的简易聊天室
- UDP传输:简易聊天室的搭建。。。
- 基于UDP的简易聊天室(服务器端)
- 基于UDP的简易聊天室(客户端)
- 使用Network实现简易聊天室
- 多人聊天室(基于Linux的SOCKET UDP编程)
- J2ME:使用UDP实现聊天室
- Linux C 多人网络聊天室(UDP)
- 简易多人聊天室
- linux下 socket的多人聊天室
- Linux下实现聊天室
- 网络通信之简易聊天室(UDP实现)
- Linux下C实现的聊天室
- Linux下C实现的聊天室
- Linux下C实现的聊天室
- file 相关
- Java中的IO流
- 第七章
- android集成微信支付
- 基本乐理知识
- linux下使用UDP实现简易的多人聊天室
- 4725: [POI2017]Reprezentacje ró?nicowe
- FIddler : CustomRules.js脚本解析get请求被加密的参数
- Spring Aop 日志管理
- ACM-二分贪心U-21
- 数据结构之一元多项式相乘
- CentOS 6.5 Git服务器搭建
- LibGDX
- 苍木之凭