思维导图
练习题
1> UDP传输实现聊天室
服务器端
#include <myhead.h>
#define SER_IP "192.168.125.151"
#define SER_PORT 9999
typedef struct Msg
{char user[32]; //用户名int type; //执行操作1.登录、2.发消息、0.退出char text[1024]; //消息内容
} msg_t;
typedef struct List
{struct sockaddr_in cin; //客户端的网络信息结构体struct List *next; //链表指针,指向下一个
} * list;struct sockaddr_in cin;
//创建头节点
list list_create()
{list p = (list)malloc(sizeof(struct List));if (p == NULL){perror("create list error");}p->next = NULL;p = NULL;
}
//线程函数事件 向所有客户端发送消息
void *task(void *arg)
{int *sockfd = (int *)arg;msg_t msg;strcpy(msg.user, "*system*");while (1){scanf("%s", msg.text);getchar();if (strncmp(msg.text, "quit", 4) == 0){exit(0);}sendto(*sockfd, msg.text, sizeof(msg), 0, (struct sockaddr *)&cin, sizeof(cin));}
}
//登录事件处理
void login(int sockfd, msg_t msg, list p, struct sockaddr_in cin)
{//新客户端插入链表list new = NULL;new = (list)malloc(sizeof(struct List));sprintf(msg.text, "login");while (p->next != NULL){//发送给的其他客户端登录消息p = p->next;sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));printf("[%s:%d]:%s login\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), msg.user);}//新节点的数据域填充新客户端地址结构体,尾插new->cin = cin;p->next = new;new->next = NULL;
}
//接收客户端消息事件处理
void chatmsg(int sockfd, msg_t msg, list p, struct sockaddr_in cin)
{//将客户端发来的消息发送给其他客户端while (p->next != NULL){p = p->next;sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));}
}
//客户端退出消息事件处理
void quit(int sockfd, msg_t msg, list p, struct sockaddr_in cin)
{list del = NULL;sprintf(msg.text, "%s out", msg.user);while (p->next != NULL){//遍历链表找到要退出的客户端地址结构体的前一个if (memcmp(&(p->next->cin), &cin, sizeof(cin)) == 0){del = p->next;p->next = del->next;free(del);del = NULL;}else{p = p->next;sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));}}
}
int main(int argc, char const *argv[])
{msg_t msg;//创建套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == -1){perror("socket error");return -1;}struct sockaddr_in sin;//填充服务器地址结构体sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);//绑定服务器if (bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) == -1){perror("bind error");return -1;}//创建客户端地址结构体struct sockaddr_in cin;//获取客户端地址结构体大小socklen_t socklen = sizeof(cin);//创建链表节点list p = list_create();//创建线程pthread_t tid;if (pthread_create(&tid, NULL, task, &sockfd) == -1){printf("pthread_create error\n");return -1;}//分离线程pthread_detach(tid);//接收客户端消息while (1){//接收客户端发来的消息,返回消息字符个数int res = recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, &socklen);if (res < 0){perror("recvfrom error");return -1;}//判断三种客户端状态 1登录 2消息 0退出if (msg.type == 1){login(sockfd, msg, p, cin);}else if (msg.type == 2){chatmsg(sockfd, msg, p, cin);}else if (msg.type == 0){printf("[%s:%d] %s out\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), msg.user);quit(sockfd, msg, p, cin);}}//关闭套接字close(sockfd);return 0;
}
客户端
#include <myhead.h>
#define SER_IP "192.168.125.151"
#define SER_PORT 9999
typedef struct Msg
{char user[32]; //用户名int type; //执行操作1.登录、2.发消息、0.退出char text[1024]; //消息内容
} msg_t;
int main(int argc, char const *argv[])
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == -1){perror("sock error");return -1;}msg_t msg;struct sockaddr_in cin;cin.sin_family = AF_INET;cin.sin_addr.s_addr = inet_addr(SER_IP);cin.sin_port = htons(SER_PORT);socklen_t socklen = sizeof(cin);char buf[128] = "";msg.type = 1;printf("please imput your name:");scanf("%s", msg.user);getchar();sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, socklen);pid_t pid = fork();if (pid < 0){perror("fork error");return -1;}else if (pid == 0) //子进程循环发送消息{while (1){printf("---------------------\n");scanf("%s", msg.text);getchar();if (strncmp(msg.text, "quit", 4) == 0){msg.type = 0;sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, socklen);kill(pid, SIGINT);exit(0);wait(NULL);}else{msg.type = 2;}sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, socklen);}}else //父进程循环接受消息{int res;while (1){res = recv(sockfd, &msg, sizeof(msg), 0);if (res == -1){perror("recv error");return -1;}printf("[%s]:%s\n", msg.user, msg.text);}wait(NULL);}close(sockfd);return 0;
}