当前位置:   article > 正文

坐牢第二十七天(聊天室)

坐牢第二十七天(聊天室)

基于UDP的网络聊天室

一.项目需求:

1.如果有用户登录,其他用户可以收到这个人的登录信息

2.如果有人发送信息,其他用户可以收到这个人的群聊信息

3.如果有人下线,其他用户可以收到这个人的下线信息

4.服务器可以发送系统信息

二.代码 

udp.h

  1. #ifndef UDP_H
  2. #define UDP_H
  3. #include <myhead.h>
  4. #define SER_PORT 8888 // 服务器端口号
  5. #define SER_IP "192.168.0.105" // 服务器ip地址
  6. #define CLI_PORT 5555 // 客户端端口号
  7. #define CLI_IP "192.168.0.105" // 客户端地址
  8. //枚举
  9. enum type_t
  10. {
  11. Login,
  12. Chat,
  13. Quit,
  14. };
  15. typedef struct MSG
  16. {
  17. char type;//Login名字 Chat内容 Quit退出 //内容编号
  18. char name[32];//名字
  19. char text[128];//内容
  20. }msg_t;
  21. typedef struct NODE//链表
  22. {
  23. struct sockaddr_in cin;
  24. struct NODE *next;
  25. }Node,*Nodeptr;
  26. //创建头节点函数
  27. Nodeptr create();
  28. //登录的函数
  29. //功能:
  30. //1.将新登录的用户转发给所有已经登录的用户(遍历链表发送谁登录的消息)
  31. //2.创建新节点来保存新登录用户的信息,链接到链表尾就可以
  32. void do_login(int sockfd,msg_t msg,Nodeptr p,struct sockaddr_in cin);
  33. //群聊的函数
  34. //功能:将客户端发来的聊天内容转发给所有已登录的用户,除了发送聊天内容的用户以外
  35. void do_chat(int sockfd,msg_t msg,Nodeptr p,struct sockaddr_in cin);
  36. //退出函数
  37. //功能:
  38. //1.将谁退出的消息转发给i所有用户
  39. //2.将链表中保存这个推出的用户信息的节点删除
  40. void do_quit(int sockfd,msg_t msg,Nodeptr p,struct sockaddr_in cin);
  41. #endif

udp.c

  1. #include "udp.h"
  2. // 定义创建头节点函数
  3. Nodeptr create()
  4. {
  5. Nodeptr p = (Nodeptr)malloc(sizeof(Node));
  6. if (p == NULL)
  7. {
  8. perror("malloc error");
  9. return NULL;
  10. }
  11. p->next = NULL;
  12. return p;
  13. }
  14. // 定义登录的函数
  15. void do_login(int sockfd, msg_t msg, Nodeptr p, struct sockaddr_in cin)
  16. {
  17. sprintf(msg.text, "%s 以上线", msg.name);
  18. while (p->next != NULL)
  19. {
  20. p = p->next;
  21. sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));
  22. }
  23. Nodeptr new = (Nodeptr)malloc(sizeof(Node));
  24. // 初始化
  25. new->cin = cin;
  26. new->next = NULL;
  27. // 链接到链表尾
  28. p->next = new;
  29. return;
  30. }
  31. // 定义群聊的函数
  32. void do_chat(int sockfd, msg_t msg, Nodeptr p, struct sockaddr_in cin)
  33. {
  34. // 遍历链表
  35. while (p->next != NULL)
  36. {
  37. p = p->next;
  38. if (memcmp(&(p->cin), &cin,sizeof(cin))!= 0)
  39. {
  40. sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));
  41. }
  42. }
  43. return;
  44. }
  45. // 定义退出函数
  46. void do_quit(int sockfd, msg_t msg, Nodeptr p, struct sockaddr_in cin)
  47. {
  48. sprintf(msg.text, "%s 以下线", msg.name);
  49. while (p->next != NULL)
  50. {
  51. if (memcmp(&(p->cin), &cin,sizeof(cin)) == 0)
  52. {
  53. Nodeptr q = NULL;
  54. q = p->next;
  55. p->next = q->next;
  56. free(q);
  57. q = NULL;
  58. }
  59. else
  60. {
  61. p = p->next;
  62. sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));
  63. }
  64. }
  65. return;
  66. }

sen.c

  1. // 服务器
  2. #include "udp.h"
  3. int main(int argc, char const *argv[])
  4. {
  5. // 创建UDP套接字
  6. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  7. if (sockfd < 0)
  8. {
  9. perror("socket error");
  10. exit(-1);
  11. }
  12. // 填充服务器网络信息结构体
  13. //定义服务器结构体
  14. struct sockaddr_in sin;
  15. sin.sin_family=AF_INET;
  16. sin.sin_port = htons(SER_PORT);
  17. sin.sin_addr.s_addr = inet_addr(SER_IP);
  18. // 定义保存客户端网络信息的结构体
  19. struct sockaddr_in cin;
  20. cin.sin_family = AF_INET;
  21. cin.sin_port = htons(CLI_PORT);
  22. cin.sin_addr.s_addr = inet_addr(CLI_IP);
  23. socklen_t len = sizeof(cin);
  24. // 绑定套接字和服务器网络信息的结构体
  25. bind(sockfd, (struct sockaddr *)&sin, sizeof(sin));
  26. printf("绑定成功!\n");
  27. msg_t msg;
  28. Nodeptr p = create();
  29. char s[20]="";
  30. while (1)
  31. {
  32. if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, &len) < 0)
  33. {
  34. perror("recvfrom error");
  35. return -1;
  36. }
  37. if (msg.type == Login)
  38. {
  39. strcpy(msg.text, "以上线");
  40. printf("ip:%s pord:%d name:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), msg.name);
  41. printf("状态:%s\n", msg.text);
  42. //调用登录函数
  43. do_login(sockfd, msg, p, cin);
  44. }
  45. else if (msg.type == Chat)
  46. {
  47. //调用群聊函数
  48. do_chat(sockfd, msg, p, cin);
  49. }
  50. else if (msg.type == Quit)
  51. {
  52. strcpy(msg.text, "以下线");
  53. printf("ip:%s pord:%d name:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), msg.name);
  54. printf("状态:%s\n", msg.text);
  55. //调用退出函数
  56. do_quit(sockfd, msg, p, cin);
  57. }
  58. }
  59. close(sockfd);
  60. return 0;
  61. }

rec.c

  1. // 客户端
  2. #include "udp.h"
  3. int main(int argc, char const *argv[])
  4. {
  5. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  6. if (sockfd < 0)
  7. {
  8. perror("socket error");
  9. exit(-1);
  10. }
  11. struct sockaddr_in sin;
  12. sin.sin_family = AF_INET;
  13. sin.sin_port = htons(SER_PORT);
  14. sin.sin_addr.s_addr = inet_addr(SER_IP);
  15. socklen_t len = sizeof(sin);
  16. msg_t msg;
  17. // 先执行登录操作
  18. printf("请登录:\n");
  19. msg.type = Login;
  20. printf("请输入用户名:");
  21. fgets(msg.name, 32, stdin);
  22. msg.name[strlen(msg.name) - 1] = 0;
  23. // 发送登录消息
  24. if (sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len) < 0)
  25. {
  26. perror("sendto err");
  27. exit(-1);
  28. }
  29. //创建多进程
  30. pid_t pid = fork();
  31. if (pid < 0)
  32. {
  33. perror("fork error");
  34. exit(-1);
  35. }
  36. else if (pid == 0)
  37. {
  38. while (1)
  39. {
  40. if (recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL) < 0)
  41. {
  42. perror("recvfrom error");
  43. return -1;
  44. }
  45. printf("[%s]:%s\n", msg.name, msg.text);
  46. }
  47. }
  48. else
  49. {
  50. while (1)
  51. {
  52. fgets(msg.text, sizeof(msg.text), stdin);
  53. msg.text[strlen(msg.text) - 1] = 0;
  54. if (strcmp(msg.text, "quit") == 0)
  55. {
  56. msg.type = Quit;
  57. sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len);
  58. kill(pid, SIGKILL);
  59. wait(NULL);
  60. exit(EXIT_SUCCESS);
  61. }
  62. else
  63. {
  64. msg.type = Chat;
  65. }
  66. // 发送消息
  67. sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len);
  68. }
  69. }
  70. close(sockfd);
  71. return 0;
  72. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/998580
推荐阅读
相关标签
  

闽ICP备14008679号