0


网络聊天室

一、项目要求

利用UDP协议,实现一套聊天室软件。服务器端记录客户端的地址,客户端发送消息后,服务器群发给各个客户端软件。

问题思考

  • 客户端会不会知道其它客户端地址?

UDP客户端不会直接互连,所以不会获知其它客户端地址,所有客户端地址存储在服务器端。

  • 有几种消息类型?
  • 登录:服务器存储新的客户端的地址。把某个客户端登录的消息发给其它客户端。
  • 聊天:服务器只需要把某个客户端的聊天消息转发给所有其它客户端。
  • 退出:服务器删除退出客户端的地址,并把退出消息发送给其它客户端。
  • 服务器如何存储客户端的地址?

数据结构可以选择线性数据结构

链表节点结构体:

structnode{

  1. structsockaddr_in addr;//data memcmp
  2. structnode*next;

};

消息对应的结构体(同一个协议)

typedefstructmsg_t

{

  1. int type;//'L' C Q enum un{login,chat,quit};
  2. char name[32];//用户名
  3. char text[128];//消息正文

}MSG_t;

intmemcmp(voids1,voids2,int size)

  • 客户端如何同时处理发送和接收?

客户端不仅需要读取服务器消息,而且需要发送消息。读取需要调用recvfrom,发送需要先调用gets,两个都是阻塞函数。所以必须使用多任务来同时处理,可以使用多进程或者多线程来处理。

二、程序流程图

服务器端

客户端

​三、代码实现

server.c代码部分:

  1. #include<stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <arpa/inet.h>
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. #include <netinet/ip.h>
  8. #include<unistd.h>
  9. #include<arpa/inet.h>
  10. #include<string.h>
  11. #include<stdlib.h>
  12. #include <sys/wait.h>
  13. #include<dirent.h>
  14. #include<sys/stat.h>
  15. #include<signal.h>
  16. #include <pthread.h>
  17. struct sockaddr_in serveraddr,caddr;
  18. enum type_t//枚举
  19. {
  20. Login,
  21. Chat,
  22. Quit,
  23. };
  24. typedef struct MSG
  25. {
  26. char type;//L C Q
  27. char name[32];//
  28. char text[128];//
  29. }msg_t;
  30. typedef struct NODE//链表
  31. {
  32. struct sockaddr_in caddr;
  33. struct NODE *next;
  34. }node_t;
  35. node_t *create_node(void)//建头节点
  36. {
  37. node_t *p=(node_t *)malloc(sizeof(node_t));
  38. if(p==NULL)
  39. {
  40. perror("malloc err");
  41. return NULL;
  42. }
  43. p->next=NULL;
  44. return p;
  45. }
  46. void do_login(int ,msg_t ,node_t *,struct sockaddr_in);//登录的函数
  47. void do_chat(int ,msg_t ,node_t *,struct sockaddr_in);//群聊的函数
  48. void do_quit(int ,msg_t ,node_t *,struct sockaddr_in);//退出函数
  49. int main(int argc, char const *argv[])
  50. {
  51. if(argc !=3)
  52. {
  53. printf("Usage:./a.out <port>\n");
  54. return -1;
  55. }
  56. //创建UDP套接字
  57. int sockfd = socket(AF_INET,SOCK_DGRAM,0);
  58. if(sockfd<0)
  59. {
  60. perror("socket err");
  61. exit(-1);
  62. }
  63. //填充服务器网络信息结构体
  64. serveraddr.sin_family=AF_INET;
  65. serveraddr.sin_port=htons(atoi(argv[2]));
  66. serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
  67. socklen_t len = sizeof(caddr);
  68. //定义保存客户端网络信息的结构体
  69. //绑定套接字和服务器网络信息的结构体
  70. bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
  71. printf("bind ok!\n");
  72. msg_t msg;
  73. node_t *p=create_node();
  74. while(1)
  75. {
  76. if(recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&caddr,&len)<0)
  77. {
  78. perror("recvfrom err");
  79. return -1;
  80. }
  81. if(msg.type==Login)
  82. {
  83. strcpy(msg.text,"以上线");
  84. printf("ip:%s pord:%d name:%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),msg.name);
  85. printf("状态:%s\n",msg.text);
  86. do_login(sockfd,msg,p,caddr);
  87. }
  88. else if(msg.type==Chat)
  89. {
  90. do_chat(sockfd,msg,p,caddr);
  91. }
  92. else if(msg.type==Quit)
  93. {
  94. strcpy(msg.text,"以下线");
  95. printf("ip:%s pord:%d name:%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),msg.name);
  96. printf("状态:%s\n",msg.text);
  97. do_quit(sockfd,msg,p,caddr);
  98. }
  99. }
  100. close(sockfd);
  101. return 0;
  102. }
  103. //登录的函数
  104. //功能:
  105. //1》将新登录的用户转发给所有已经登录的用户(遍历链表发送谁登录的消息)
  106. //2》创建新节点来保存新登录用户的信息,链接到链表尾就可以
  107. void do_login(int sockfd,msg_t msg,node_t *p,struct sockaddr_in caddr)
  108. {
  109. sprintf(msg.text,"%s 以上线",msg.name);
  110. while(p->next != NULL)
  111. {
  112. p= p->next;
  113. sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->caddr),sizeof(p->caddr));
  114. //printf("%s\n",msg.text);
  115. }
  116. node_t *new=(node_t *)malloc(sizeof(node_t));
  117. //初始化
  118. new->caddr=caddr;
  119. new->next=NULL;
  120. //链接到链表尾
  121. p->next=new;
  122. return;
  123. }
  124. //群聊的函数
  125. //功能:将客户端发来的聊天内容转发给所有已登录的用户,除了发送聊天内容的用户以外
  126. void do_chat(int sockfd,msg_t msg,node_t *p,struct sockaddr_in caddr)
  127. {
  128. //遍历链表
  129. while(p->next != NULL)
  130. {
  131. p=p->next;
  132. if(memcmp(&(p->caddr),&caddr,sizeof(caddr)) != 0)
  133. {
  134. sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->caddr),sizeof(p->caddr));
  135. }
  136. }
  137. return;
  138. }
  139. //退出函数
  140. //功能:
  141. //1》将谁退出的消息转发给i所有用户
  142. //2》将链表中保存这个推出的用户信息的节点删除
  143. void do_quit(int sockfd,msg_t msg,node_t *p,struct sockaddr_in caddr)
  144. {
  145. sprintf(msg.text,"%s 以下线",msg.name);
  146. while(p->next != NULL)
  147. {
  148. if((memcmp(&(p->next->caddr),&caddr,sizeof(caddr)))==0)
  149. {
  150. node_t *dele=NULL;
  151. dele = p->next;
  152. p->next=dele->next;
  153. free(dele);
  154. dele=NULL;
  155. }
  156. else
  157. {
  158. p=p->next;
  159. sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->caddr),sizeof(p->caddr));
  160. }
  161. }
  162. return;
  163. }

client.c代码部分:

  1. #include<stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <arpa/inet.h>
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. #include <netinet/ip.h>
  8. #include<unistd.h>
  9. #include<arpa/inet.h>
  10. #include<string.h>
  11. #include<stdlib.h>
  12. #include <sys/wait.h>
  13. #include<dirent.h>
  14. #include<sys/stat.h>
  15. enum type_t
  16. {
  17. Login,
  18. Chat,
  19. Quit,
  20. };
  21. typedef struct
  22. {
  23. char type;//L C Q
  24. char name[32];//
  25. char text[128];//
  26. }msg_t;
  27. int main(int argc, char const *argv[])
  28. {
  29. if(argc !=3)
  30. {
  31. printf("Usage ./a.out <ip> <port>\n");
  32. return -1;
  33. }
  34. int sockfd = socket(AF_INET,SOCK_DGRAM,0);
  35. if(sockfd<0)
  36. {
  37. perror("socket err");
  38. exit(-1);
  39. }
  40. struct sockaddr_in serveraddr;
  41. serveraddr.sin_family=AF_INET;
  42. serveraddr.sin_port=htons(atoi(argv[2]));
  43. serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
  44. socklen_t len = sizeof(serveraddr);
  45. msg_t msg;
  46. //先执行登录操作
  47. printf("请登录:\n");
  48. msg.type=Login;
  49. printf("请输入用户名:");
  50. fgets(msg.name,32,stdin);
  51. if(msg.name[strlen(msg.name)-1]=='\n')
  52. msg.name[strlen(msg.name)-1]='\0';
  53. //发送登录消息
  54. if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&serveraddr,len)<0)
  55. {
  56. perror("sendto err");
  57. exit(-1);
  58. }
  59. pid_t pid=fork();
  60. if(pid<0)
  61. {
  62. perror("fork err");
  63. exit(-1);
  64. }
  65. else if(pid==0)
  66. {
  67. while(1)
  68. {
  69. if(recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL)<0)
  70. {
  71. perror("recvfrom err");
  72. return -1;
  73. }
  74. printf("[%s]:%s\n",msg.name,msg.text);
  75. }
  76. }
  77. else
  78. {
  79. while(1)
  80. {
  81. fgets(msg.text,sizeof(msg.text),stdin);
  82. if(msg.text[strlen(msg.text)-1]=='\n')
  83. msg.text[strlen(msg.text)-1]='\0';
  84. if(strcmp(msg.text,"quit")==0)
  85. {
  86. msg.type=Quit;
  87. sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&serveraddr,len);
  88. kill(pid,SIGKILL);
  89. wait(NULL);
  90. exit(-1);
  91. }else
  92. {
  93. msg.type=Chat;
  94. }
  95. //发送消息
  96. sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&serveraddr,len);
  97. }
  98. }
  99. close(sockfd);
  100. return 0;
  101. }
标签: 网络 udp 网络协议

本文转载自: https://blog.csdn.net/qq_52049228/article/details/132348391
版权归原作者 代码大魔王ㅤ 所有, 如有侵权,请联系我们删除。

“网络聊天室”的评论:

还没有评论