将前面的数据结构,多线程,网络的内容加在一起的一个项目,比较综合,在代码部分采用了分文件编译并且写了比较详细的注释(个人觉得)。
ps:希望对大家有用
#ifndef CHATSER_H
#define CHATSER_H
#include
//从客户端接收消息结构体
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
//创建发送消息结构体
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;
}
项目效果图