赞
踩
将前面的数据结构,多线程,网络的内容加在一起的一个项目,比较综合,在代码部分采用了分文件编译并且写了比较详细的注释(个人觉得)。
ps:希望对大家有用
- #ifndef CHATSER_H
- #define CHATSER_H
- #include<myhead.h>
- //从客户端接收消息结构体
- typedef struct msgTyp
- {
- char type;//消息类型
- char userName[20];//用户名
- char msgText[1024];//消息数据
- }msgTyp,*msgTypPtr;
-
- //用户信息
- typedef struct users
- {
- char userName[20];//用户名
- struct sockaddr_in cin;//客户端的信息
- }Users,*UsersPtr;
- //链表的结构体
- typedef struct Node
- {
- union
- {
- int len;//链表长度
- UsersPtr data;//用户信息
- };
- struct Node *next;//指针域
- }Node, *NodePtr;
- //创建传递给线程函数的结构体
- typedef struct argTyp
- {
- NodePtr L;//链表
- struct sockaddr_in cin;//客户端的信息
- struct sockaddr_in sin;//服务器的信息
- int sfd;//套接字
- msgTyp msg;//客户端接收消息结构体
- }argTyp,*argTypPtr;
-
- //创建链表
- NodePtr carete_link();
- //创建节点
- NodePtr create_node(char *username,struct sockaddr_in cin);
- //服务器发送消息
- void *send_msg(void *arg);
- //服务器接收并转发消息
- void *recv_msg(void *arg);
- //删除节点
- void delete_node(NodePtr L, char *username);
-
-
- #endif
- #include"chatser.h"
- //创建链表
- NodePtr carete_link()
- {
- //创建头节点
- NodePtr L = (NodePtr)malloc(sizeof(Node));
- if (NULL == L)
- {
- printf("创建头节点失败\n");
- return NULL;
- }
- //初始化头节点
- L->len = 0;
- L->next = NULL;
- return L;
- }
- //创建节点
- NodePtr create_node(char *username,struct sockaddr_in cin)
- {
- //创建节点
- NodePtr p =(NodePtr)malloc(sizeof(Node));
- if (NULL == p)
- {
- printf("创建节点失败\n");
- return NULL;
- }
- //初始化节点信息
- p->data = (UsersPtr)malloc(sizeof(Users));//给data用户信息分配空间
- strcpy(p->data->userName,username);//将用户名复制到data的username
- p->data->cin = cin;//将客户端信息复制到data的cin
- p->next = NULL;
- return p;
- }
- //服务器发送消息
- void *send_msg(void *arg)//传入线程函数的结构体
- {
- //获取结构体
- argTypPtr argTyp = (argTypPtr)arg;//结构体
- NodePtr L = argTyp->L;//链表
- struct sockaddr_in sin = argTyp->sin;//服务器信息
- int sfd = argTyp->sfd;//socket
- while (1)
- {
- //从终端输入消息
- char buf[1024] = "";//未处理消息
- char wbuf[1024] = "";//处理后的消息
- fgets(buf,sizeof(buf),stdin);
- buf[strlen(buf)-1] = 0;
- //处理消息
- snprintf(wbuf,sizeof(wbuf),"**system**:%s",buf);
- //遍历链表发送消息
- NodePtr p = L->next;
- while(p!=NULL)
- {
- if(sendto(sfd,wbuf,strlen(wbuf),0,(struct sockaddr*)&p->data->cin,sizeof(p->data->cin)) < 0)
- {
- printf("发送消息失败\n");
- return NULL;
- }
- p = p->next;
- }
- printf("**system** [%s:%d]:chat成功\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
- }
-
-
- pthread_exit(NULL);//退出线程
- return NULL;
- }
- //服务器接收并转发消息
- void *recv_msg(void *arg)//传入线程函数的结构体
- {
- //获取结构体
- argTypPtr argTyp = (argTypPtr)arg;
- NodePtr L = argTyp->L;//链表
- struct sockaddr_in cin = argTyp->cin;//客户端信息
- int sfd = argTyp->sfd;//socket
- char username[20] = "";//用户名
- char buf[1024] = "";//未处理的消息
- char wbuf[1024] = "";//处理后的消息
-
- //获取用户名
- strcpy(username,argTyp->msg.userName);
-
- //获取是什么类型的消息
- if(argTyp->msg.type == 'L')//登录
- {
-
- //判断是否有该用户
- NodePtr q = L->next;
- // while(q!=NULL)
- // {
- // if(strcmp(q->data->userName,username) == 0)
- // {
- // printf("该用户已存在\n");
- // break;
- // return NULL;
- // }
- // }
- //创建节点
- NodePtr p = create_node(username,cin);
- //将节点添加到链表(我采用头插)
- p->next = L->next;
- L->next = p;
- L->len++;//链表长度加1
- //处理消息
- snprintf(wbuf,sizeof(wbuf),"**%s已登录**",username);
- //遍历链表发送消息
- p = L->next;
- while(p!=NULL)
- {
- if(sendto(sfd,wbuf,strlen(wbuf),0,(struct sockaddr*)&p->data->cin,sizeof(p->data->cin)) < 0)
- {
- printf("发送消息失败\n");
- return NULL;
- }
- p = p->next;
- }
- printf("%s [%s:%d]:登录成功\n", username, inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
- }else if(argTyp->msg.type == 'C')//聊天
- {
- //处理消息
- snprintf(wbuf,sizeof(wbuf),"%s:%s",username,argTyp->msg.msgText);
- //遍历链表转发消息
- NodePtr p = L->next;
- while(p!=NULL)
- {
- if(sendto(sfd,wbuf,strlen(wbuf),0,(struct sockaddr*)&p->data->cin,sizeof(p->data->cin)) < 0)
- {
- printf("发送消息失败\n");
- return NULL;
- }
- p = p->next;
- }
- printf("%s [%s:%d]:chat成功\n", username, inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
- }else if(argTyp->msg.type == 'Q')//退出
- {
- //删除节点
- delete_node(L,username);
- //处理消息
- snprintf(wbuf,sizeof(wbuf),"**%s已退出**",username);
- //循环遍历链表发送消息
- NodePtr p = L->next;
- while(p!=NULL)
- {
- if(sendto(sfd,wbuf,strlen(wbuf),0,(struct sockaddr*)&p->data->cin,sizeof(p->data->cin)) < 0)
- {
- printf("发送消息失败\n");
- return NULL;
- }
- p = p->next;
- }
- printf("%s [%s:%d]:退出成功\n", username, inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
- L->len--;//链表长度减1
- }
- pthread_exit(NULL);//退出线程
- return NULL;
- }
- //删除节点
- void delete_node(NodePtr L, char *username)
- {
- //删除节点
- void delete_node(NodePtr L, char *username)
- {
- //找到要删除节点的前驱节点
- NodePtr p = L;
- while (p->next != NULL && strcmp(p->next->data->userName, username) != 0)
- {
- p = p->next;
- }
- //删除节点
- if (p->next != NULL)
- {
- NodePtr temp = p->next; // 保存要删除的节点
- p->next = p->next->next; // 删除节点
- free(temp->data); // 释放节点数据
- free(temp); // 释放节点
- }
- }
- }
- #include"chatser.h"
- int main(int argc, const char *argv[])
- {
-
- //创建链表
- NodePtr L = carete_link();
-
- //创建管道
- int sfd = socket(AF_INET,SOCK_DGRAM,0);
- if (sfd == -1)
- {
- perror("socket");
- return 1;
- }
- if(argc != 3)
- {
- printf("请输入ip地址和端口号\n");//argv[1]是 ip地址 argv[2]是端口号
- return 1;
- }
- //填充服务端地址结构体
- struct sockaddr_in sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(atoi(argv[2]));
- sin.sin_addr.s_addr = inet_addr(argv[1]);
- //绑定
- if (bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1)
- {
- perror("bind error");
- return 1;
- }
-
-
- //获取客户端地址结构体
- struct sockaddr_in cin;
- socklen_t len = sizeof(cin);
-
- //线程实现收发数据
- pthread_t pid1,pid2;
-
-
- //填充结构体信息
- struct argTyp arg = {L,cin,sin,sfd};
- //创建发送线程
- if(pthread_create(&pid1,NULL,send_msg,&arg)==-1)
- {
- perror("pthread_create error");
- return 1;
- }
- //将线程设置为分离状态
- pthread_detach(pid1);
- while(1)
- {
- //接收客户端发送的数据
- if(recvfrom(sfd,&arg.msg,sizeof(arg.msg),0,(struct sockaddr*)&cin,&len)==-1)
- {
- perror("recvfrom error");
- return 1;
- }
- //填充结构体
- struct argTyp arg1 = {L,cin,sin,sfd};
-
- arg1.msg = arg.msg;
-
- //创建接收线程
- if(pthread_create(&pid2,NULL,recv_msg,&arg1)==-1)
- {
- perror("pthread_create error");
- return 1;
- }
- //将线程设置为分离状态
- //pthread_detach(pid1);
- pthread_detach(pid2);
- }
- close(sfd);
- return 0;
- }
- #ifndef CHATCLI_H
- #define CHATCLI_H
- #include<myhead.h>
- //创建发送消息结构体
- typedef struct msgTyp
- {
- char type;//消息类型
- char userName[20];//用户名
- char msgText[1024];//消息数据
- }msgTyp,*msgTypPtr;
-
- //创建线程函数传输的结构体
- typedef struct argTyp
- {
- struct sockaddr_in sin;//服务器的信息
- int cfd;//套接字
- msgTyp msg;//客户端接收消息结构体
- }argTyp,*argTypPtr;
-
-
- //客户端发送消息
- void *send_msg(void *arg);
- //客户端接收并转发消息
- void *recv_msg(void *arg);
-
- // 将全局变量改为外部声明
- extern int flag; // 标志位判断用户是否创建
- extern char userName[20]; // 存放姓名
- extern int num; // 记录用户是否退出
-
- #endif
- #include"chatcli.h"
- // 在这里定义全局变量
- int flag = 0;
- char userName[20] = "";
- int num = 1;
-
- //客户端发送消息
- void *send_msg(void *arg)
- {
- //printf("?\n");
- argTypPtr argTyp = (argTypPtr)arg;//获取传来的结构体
- struct sockaddr_in sin = argTyp->sin;
- int cfd = argTyp->cfd;
- char msgText[1024]="";//存放消息内容
- char buf[1024]="";//用来中转的
- while (num == 1)
- {
- fgets(buf,sizeof(buf),stdin);//获取终端传来的数据
- buf[strlen(buf)-1]=0;
- //表示该用户未注册
- if(flag ==0)
- {
- //printf("%d\n",flag);
- argTyp->msg.type = 'L';//发送的消息类型为登录
- strcpy(userName, buf);//将用户名记录下来
- //printf("%d\n",flag);
- strcpy(argTyp->msg.userName, buf);//给用户名赋值
- //printf("%s\n",argTyp->msg.userName);
- strcpy(argTyp->msg.msgText, msgText);//给消息数据赋值
- //printf("%d\n",flag);
- if (sendto(cfd, &argTyp->msg, sizeof(argTyp->msg), 0, (struct sockaddr*)&sin, sizeof(sin)) == -1)
- {
- perror("sendto error");
- return NULL;
- }
- flag++;//表示用户已经注册
- //printf("%d\n",flag);
- }else if (flag > 0)
- {
- if(strcmp(buf,"quit")==0)
- {
- argTyp->msg.type = 'Q';//发送的消息类型为登出
- strcpy(argTyp->msg.userName,userName);//给用户名赋值
- strcpy(argTyp->msg.msgText,msgText);//给消息数据赋值
- if(sendto(cfd,&argTyp->msg,sizeof(argTyp->msg),0,(struct sockaddr*)&sin,sizeof(sin))==-1)
- {
- perror("sendto error");
- return NULL;
- }
- num = 0;
- //表示退出
- break;
- }
- argTyp->msg.type = 'C';//发送的消息类型为聊天
- strcpy(argTyp->msg.userName,userName);//给用户名赋值
- strcpy(argTyp->msg.msgText,buf);//给消息数据赋值
- if(sendto(cfd,&argTyp->msg,sizeof(argTyp->msg),0,(struct sockaddr*)&sin,sizeof(sin))==-1)
- {
- perror("sendto error");
- return NULL;
- }
-
-
- }
- }
-
-
- pthread_exit(NULL);
- }
- //客户端接收消息
- void *recv_msg(void *arg)
- {
- argTypPtr argTyp = (argTypPtr)arg;//接收传来的结构体
- int cfd = argTyp->cfd;//获取套接字
- char buf[1024]="";//存放消息内容
- while (num == 1)
- {
- bzero(buf,sizeof(buf));//清空容器
- if(recvfrom(cfd,buf,sizeof(buf),0,NULL,NULL)==-1)
- {
- perror("recvfrom error");
- return NULL;
- }
- printf("%s\n",buf);
- }
- pthread_exit(NULL);
- }
- #include"chatcli.h"
- // 声明 num 为外部变量
- extern int num;
- int main(int argc, const char *argv[])
- {
-
-
- //创建管道
- int cfd = socket(AF_INET,SOCK_DGRAM,0);
- if (cfd == -1)
- {
- perror("socket");
- return 1;
- }
- //判断输入的参数是否正确
- if(argc != 3)
- {
- printf("请输入ip地址和端口号\n");//argv[1]是 ip地址 argv[2]是端口号
- return 1;
- }
- //填充服务端地址结构体
- struct sockaddr_in sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(atoi(argv[2]));
- sin.sin_addr.s_addr = inet_addr(argv[1]);
- //创建线程
- pthread_t pid1,pid2;
- //结构体赋值
- struct argTyp arg = {sin,cfd};
-
- //创建发送线程
- if(pthread_create(&pid1,NULL,send_msg,&arg) != 0)
- {
- printf("创建线程失败\n");
- return -1;
- }
- //创建接收线程
- if(pthread_create(&pid2,NULL,recv_msg,&arg) != 0)
- {
- printf("创建线程失败\n");
- return -1;
- }
-
- //设置线程分离
- pthread_detach(pid1);
- pthread_detach(pid2);
- while (num);
- close(cfd);
- return 0;
- }
项目效果图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。