• UDP聊天室项目


    代码思路

    服务器

    1. #include
    2. #include /* See NOTES */
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. //链表节点结构体:
    13. typedef struct node
    14. {
    15. struct sockaddr_in addr; //data memcmp
    16. struct node *next;
    17. } link_t;
    18. //类型
    19. enum type_t
    20. {
    21. login,
    22. chat,
    23. quit,
    24. };
    25. //消息对应的结构体(同一个协议)
    26. typedef struct msg_t
    27. {
    28. int type; //'L' C Q enum un{login,chat,quit};
    29. char name[32]; //用户名
    30. char text[128]; //消息正文
    31. } MSG_t;
    32. link_t *link_creat();
    33. void client_login(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);
    34. void client_quit(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);
    35. void client_chat(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);
    36. int main(int argc, char const *argv[])
    37. {
    38. // 创建套接字
    39. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    40. if (sockfd < 0)
    41. {
    42. perror("socket err");
    43. return -1;
    44. }
    45. printf("sockfd:%d\n", sockfd);
    46. // 绑定
    47. struct sockaddr_in saddr, caddr;
    48. saddr.sin_family = AF_INET;
    49. saddr.sin_port = htons(atoi(argv[1]));
    50. saddr.sin_addr.s_addr = INADDR_ANY;
    51. socklen_t len = sizeof(caddr);
    52. if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    53. {
    54. perror("bind err");
    55. return -1;
    56. }
    57. printf("bind ok\n");
    58. // 创建链表
    59. link_t *p = link_creat();
    60. printf("creat ok\n");
    61. MSG_t msg;
    62. // 可扩展功能,服务器可以给所有用户发送“公告”
    63. pid_t pid = fork();
    64. if (pid < 0)
    65. {
    66. perror("fork err");
    67. return -1;
    68. }
    69. else if (pid == 0)
    70. {
    71. while (1)
    72. {
    73. fgets(msg.text, sizeof(msg.text), stdin);
    74. if (msg.text[strlen(msg.text) - 1] == '\n')
    75. msg.text[strlen(msg.text) - 1] = '\0';
    76. msg.type = chat;
    77. strcpy(msg.name, "server");
    78. sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
    79. }
    80. }
    81. else
    82. {
    83. while (1)
    84. {
    85. if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len) < 0)
    86. {
    87. perror("recvfrom err");
    88. return -1;
    89. }
    90. printf("type:%d\n", msg.type);
    91. switch (msg.type)
    92. {
    93. case login: // 上线函数
    94. client_login(sockfd, p, msg, caddr);
    95. break;
    96. case chat: // 聊天函数
    97. client_chat(sockfd, p, msg, caddr);
    98. break;
    99. case quit: // 下线函数
    100. client_quit(sockfd, p, msg, caddr);
    101. break;
    102. default:
    103. break;
    104. }
    105. }
    106. }
    107. close(sockfd);
    108. return 0;
    109. }
    110. // 创建链表
    111. link_t *link_creat()
    112. {
    113. // 给头结点开辟空间
    114. link_t *p = (link_t *)malloc(sizeof(link_t));
    115. if (p == NULL)
    116. {
    117. perror("malloc err");
    118. return NULL;
    119. }
    120. // 初始化头结点
    121. p->next = NULL;
    122. return p;
    123. }
    124. // 客户端上线功能:将用户上线消息发送给其他用户,并将用户信息添加到链表中
    125. void client_login(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
    126. {
    127. printf("%s login....\n", msg.name);
    128. // 先遍历链表
    129. // 再插入新的节点
    130. while (p->next != NULL)
    131. {
    132. p = p->next;
    133. sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
    134. }
    135. link_t *pnew = (link_t *)malloc(sizeof(link_t));
    136. pnew->addr = caddr;
    137. pnew->next = NULL;
    138. p->next = pnew;
    139. return;
    140. }
    141. // 聊天功能:转发消息到在链表中除了自己的所有用户
    142. void client_chat(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
    143. {
    144. printf("%s says %s\n", msg.name, msg.text);
    145. while (p->next != NULL)
    146. {
    147. p = p->next;
    148. // strcmp:对比前后两个字符串中的内容是否一致
    149. // memcmp:对比前后两个对应的地址(任意类型)的内容是否一致
    150. if (memcmp(&(p->addr), &caddr, sizeof(caddr)))
    151. {
    152. sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
    153. }
    154. }
    155. return;
    156. }
    157. // 客户端退出功能:将用户下线消息发送给其他用户,将下线用户从链表中删除
    158. void client_quit(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
    159. {
    160. printf("%s quit.......\n", msg.name);
    161. link_t *pdel = NULL;
    162. while (p->next != NULL)
    163. {
    164. if (memcmp(&(p->next->addr), &caddr, sizeof(caddr)))
    165. {
    166. // 不是自己
    167. p = p->next;
    168. sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
    169. }
    170. else
    171. {
    172. // 是自己,删除节点
    173. pdel = p->next;
    174. p->next = pdel->next;
    175. free(pdel);
    176. pdel = NULL;
    177. }
    178. }
    179. return;
    180. }

    客户端

    1. #include
    2. #include /* See NOTES */
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. //链表节点结构体:
    13. typedef struct node
    14. {
    15. struct sockaddr_in addr; //data memcmp
    16. struct node *next;
    17. } link_t;
    18. //类型
    19. enum type_t
    20. {
    21. login,
    22. chat,
    23. quit,
    24. };
    25. //消息对应的结构体(同一个协议)
    26. typedef struct msg_t
    27. {
    28. int type; //'L' C Q enum un{login,chat,quit};
    29. char name[32]; //用户名
    30. char text[128]; //消息正文
    31. } MSG_t;
    32. int main(int argc, char const *argv[])
    33. {
    34. int ret;
    35. // 创建套接字
    36. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    37. if (sockfd < 0)
    38. {
    39. perror("socket err");
    40. return -1;
    41. }
    42. printf("sockfd:%d\n", sockfd);
    43. // 绑定
    44. struct sockaddr_in saddr, caddr;
    45. saddr.sin_family = AF_INET;
    46. saddr.sin_port = htons(atoi(argv[2]));
    47. saddr.sin_addr.s_addr = inet_addr(argv[1]);
    48. socklen_t len = sizeof(caddr);
    49. MSG_t msg;
    50. // 给服务器发送上线消息
    51. printf("请输入用户名:");
    52. fgets(msg.name, sizeof(msg.name), stdin);
    53. if (msg.name[strlen(msg.name) - 1] == '\n')
    54. msg.name[strlen(msg.name) - 1] = '\0';
    55. msg.type = login;
    56. sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
    57. pid_t pid = fork();
    58. if (pid < 0)
    59. {
    60. perror("fork err");
    61. return -1;
    62. }
    63. else if (pid == 0) // 循环接受服务器消息
    64. {
    65. while (1)
    66. {
    67. ret = recvfrom(sockfd, &msg, sizeof(MSG_t), 0, NULL, NULL);
    68. if (ret < 0)
    69. {
    70. perror("recv from err");
    71. return -1;
    72. }
    73. if (msg.type == login)
    74. printf("%s login.......\n", msg.name);
    75. else if (msg.type == chat)
    76. printf("%s says %s\n", msg.name, msg.text);
    77. else if (msg.type == quit)
    78. printf("%s quit.......\n", msg.name);
    79. }
    80. }
    81. else // 循环发送消息
    82. {
    83. while (1)
    84. {
    85. fgets(msg.text, sizeof(msg.text), stdin);
    86. if (msg.text[strlen(msg.text) - 1] == '\n')
    87. msg.text[strlen(msg.text) - 1] = '\0';
    88. if (!strcmp(msg.text, "quit"))
    89. {
    90. msg.type = quit;
    91. sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
    92. break;
    93. }
    94. else
    95. {
    96. msg.type = chat;
    97. sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
    98. }
    99. }
    100. kill(pid, SIGINT);
    101. wait(NULL);
    102. }
    103. close(sockfd);
    104. return 0;
    105. }
  • 相关阅读:
    如何开通腾讯云Redis云数据库并创建连接?
    Unity Metaverse(三)、Protobuf & Socket 实现多人在线
    shell语法(一)
    切糕 小白月赛45
    大学生游戏静态HTML网页设计 (HTML+CSS+JS仿英雄联盟网站15页)
    【云原生丨Docker系列11】Docker Machine 操作详解
    自定义Kotlin协程调度器
    RSS订阅
    基于SSM的人才招聘网站的研究与实现
    关于redis的一主三从三哨兵的实现
  • 原文地址:https://blog.csdn.net/qq_64136247/article/details/142262423