

- #include
- #include
/* See NOTES */ - #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //链表节点结构体:
- typedef struct node
- {
- struct sockaddr_in addr; //data memcmp
- struct node *next;
- } link_t;
-
- //类型
- enum type_t
- {
- login,
- chat,
- quit,
- };
-
- //消息对应的结构体(同一个协议)
- typedef struct msg_t
- {
- int type; //'L' C Q enum un{login,chat,quit};
- char name[32]; //用户名
- char text[128]; //消息正文
- } MSG_t;
-
- link_t *link_creat();
- void client_login(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);
- void client_quit(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);
- void client_chat(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);
-
- int main(int argc, char const *argv[])
- {
- // 创建套接字
- int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0)
- {
- perror("socket err");
- return -1;
- }
- printf("sockfd:%d\n", sockfd);
- // 绑定
- struct sockaddr_in saddr, caddr;
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(atoi(argv[1]));
- saddr.sin_addr.s_addr = INADDR_ANY;
- socklen_t len = sizeof(caddr);
- if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
- {
- perror("bind err");
- return -1;
- }
- printf("bind ok\n");
- // 创建链表
- link_t *p = link_creat();
- printf("creat ok\n");
- MSG_t msg;
- // 可扩展功能,服务器可以给所有用户发送“公告”
- pid_t pid = fork();
- if (pid < 0)
- {
- perror("fork err");
- return -1;
- }
- else if (pid == 0)
- {
- while (1)
- {
-
- fgets(msg.text, sizeof(msg.text), stdin);
- if (msg.text[strlen(msg.text) - 1] == '\n')
- msg.text[strlen(msg.text) - 1] = '\0';
- msg.type = chat;
- strcpy(msg.name, "server");
- sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
- }
- }
- else
- {
- while (1)
- {
- if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len) < 0)
- {
- perror("recvfrom err");
- return -1;
- }
- printf("type:%d\n", msg.type);
- switch (msg.type)
- {
- case login: // 上线函数
- client_login(sockfd, p, msg, caddr);
- break;
- case chat: // 聊天函数
- client_chat(sockfd, p, msg, caddr);
- break;
- case quit: // 下线函数
- client_quit(sockfd, p, msg, caddr);
- break;
- default:
- break;
- }
- }
- }
-
- close(sockfd);
-
- return 0;
- }
- // 创建链表
- link_t *link_creat()
- {
- // 给头结点开辟空间
- link_t *p = (link_t *)malloc(sizeof(link_t));
- if (p == NULL)
- {
- perror("malloc err");
- return NULL;
- }
- // 初始化头结点
- p->next = NULL;
- return p;
- }
-
- // 客户端上线功能:将用户上线消息发送给其他用户,并将用户信息添加到链表中
- void client_login(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
- {
- printf("%s login....\n", msg.name);
- // 先遍历链表
- // 再插入新的节点
- while (p->next != NULL)
- {
- p = p->next;
- sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
- }
- link_t *pnew = (link_t *)malloc(sizeof(link_t));
- pnew->addr = caddr;
- pnew->next = NULL;
- p->next = pnew;
-
- return;
- }
- // 聊天功能:转发消息到在链表中除了自己的所有用户
- void client_chat(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
- {
- printf("%s says %s\n", msg.name, msg.text);
- while (p->next != NULL)
- {
- p = p->next;
- // strcmp:对比前后两个字符串中的内容是否一致
- // memcmp:对比前后两个对应的地址(任意类型)的内容是否一致
- if (memcmp(&(p->addr), &caddr, sizeof(caddr)))
- {
- sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
- }
- }
-
- return;
- }
- // 客户端退出功能:将用户下线消息发送给其他用户,将下线用户从链表中删除
- void client_quit(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
- {
- printf("%s quit.......\n", msg.name);
- link_t *pdel = NULL;
- while (p->next != NULL)
- {
- if (memcmp(&(p->next->addr), &caddr, sizeof(caddr)))
- {
- // 不是自己
- p = p->next;
- sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
- }
- else
- {
- // 是自己,删除节点
- pdel = p->next;
- p->next = pdel->next;
- free(pdel);
- pdel = NULL;
- }
- }
- return;
- }
- #include
- #include
/* See NOTES */ - #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //链表节点结构体:
- typedef struct node
- {
- struct sockaddr_in addr; //data memcmp
- struct node *next;
- } link_t;
-
- //类型
- enum type_t
- {
- login,
- chat,
- quit,
- };
-
- //消息对应的结构体(同一个协议)
- typedef struct msg_t
- {
- int type; //'L' C Q enum un{login,chat,quit};
- char name[32]; //用户名
- char text[128]; //消息正文
- } MSG_t;
-
- int main(int argc, char const *argv[])
- {
- int ret;
- // 创建套接字
- int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0)
- {
- perror("socket err");
- return -1;
- }
- printf("sockfd:%d\n", sockfd);
- // 绑定
- struct sockaddr_in saddr, caddr;
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(atoi(argv[2]));
- saddr.sin_addr.s_addr = inet_addr(argv[1]);
- socklen_t len = sizeof(caddr);
-
- MSG_t msg;
- // 给服务器发送上线消息
- printf("请输入用户名:");
- fgets(msg.name, sizeof(msg.name), stdin);
- if (msg.name[strlen(msg.name) - 1] == '\n')
- msg.name[strlen(msg.name) - 1] = '\0';
- msg.type = login;
- sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
-
- pid_t pid = fork();
- if (pid < 0)
- {
- perror("fork err");
- return -1;
- }
- else if (pid == 0) // 循环接受服务器消息
- {
- while (1)
- {
- ret = recvfrom(sockfd, &msg, sizeof(MSG_t), 0, NULL, NULL);
- if (ret < 0)
- {
- perror("recv from err");
- return -1;
- }
- if (msg.type == login)
- printf("%s login.......\n", msg.name);
- else if (msg.type == chat)
- printf("%s says %s\n", msg.name, msg.text);
- else if (msg.type == quit)
- printf("%s quit.......\n", msg.name);
- }
- }
- else // 循环发送消息
- {
- while (1)
- {
- fgets(msg.text, sizeof(msg.text), stdin);
- if (msg.text[strlen(msg.text) - 1] == '\n')
- msg.text[strlen(msg.text) - 1] = '\0';
- if (!strcmp(msg.text, "quit"))
- {
- msg.type = quit;
- sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
- break;
- }
- else
- {
- msg.type = chat;
- sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
- }
- }
- kill(pid, SIGINT);
- wait(NULL);
- }
- close(sockfd);
- return 0;
- }