思维导图
服务器代码
#include<myhead.h>
#define PORT 4567
#define IP "192.168.6.225"
struct msg //接收到的客户端信息结构体
{
char type;
char name[20];
char txt[128];
};
//定义节点类型
typedef struct Node
{
union
{
struct sockaddr_in cin; //数据域
int len; //头结点数据域
};
struct Node *next; //指针域
}Node, *LinkListPtr;
struct task
{
LinkListPtr L;
int sfd;
};
//创建链表
LinkListPtr list_create();
//判空操作
int list_empty(LinkListPtr L);
//申请结点封装数据函数
LinkListPtr node_buy(struct sockaddr_in cin);
//头插
int list_insert_head(LinkListPtr L, struct sockaddr_in cin);
//遍历链表,发送数据
int list_show(LinkListPtr L, int sfd, char *buf);
void *send_cli_msg(void *arg);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket success\n");
//允许端口能被快速复用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口被快速复用成功\n");
//填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//bind函数绑定服务器地址信息
if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
struct sockaddr_in cin; //定义客户端地址信息结构体
socklen_t addrlen = sizeof(cin);
struct msg climsg; //定义存放客户端信息结构体
pthread_t tid;
LinkListPtr L = list_create();
struct task taskin;
taskin.L = L;
taskin.sfd = sfd;
while(1)
{
//接受信息
if(recvfrom(sfd, (struct msg*)&climsg, sizeof(climsg), 0, (struct sockaddr*)&cin, &addrlen) < 0)
{
ERR_MSG("recv");
return -1;
}
if(climsg.type == 'L')
{
printf("----%s----已上线\n",climsg.name); //打印客户端上线
char buf2[128];
sprintf(buf2, "----%s----已上线", climsg.name);
list_insert_head(L, cin);//加入链表
list_show(L, sfd, buf2);
}else if(climsg.type == 'C')
{
printf("%s: %s\n",climsg.name, climsg.txt);
char buf2[128];
strcpy(buf2, climsg.name);
strcat(buf2, climsg.txt);
list_show(L, sfd, buf2);
}else if(climsg.type == 'Q')
{
printf("----%s----已下线\n",climsg.name); //打印客户端上线
char buf2[128];
sprintf(buf2, "----%s----已下线", climsg.name);
list_show(L, sfd, buf2);
}
if(pthread_create(&tid, NULL, send_cli_msg, (void*)&taskin) != 0)
{
fprintf(stderr, "pthread_create failed__%d__\n", __LINE__);
return -1;
}
//线程分离
pthread_detach(tid);
}
if(close(sfd) < 0) //关闭文件描述符
{
ERR_MSG("close");
return -1;
}
return 0;
}
void *send_cli_msg(void *arg)
{
LinkListPtr L = ((struct task*)arg)->L;
int sfd = ((struct task*)arg)->sfd;
//群发消息
char buf1[128] = "";
bzero(buf1, sizeof(buf1));
scanf("%s", buf1);
list_show(L, sfd, buf1);
}
//创建链表
LinkListPtr list_create()
{
//在堆区申请一个头结点类型
LinkListPtr L = (LinkListPtr)malloc(sizeof(Node));
if(NULL == L)
{
printf("创建失败\n");
return NULL;
}
//创建成功,对节点进行初始化
L->len = 0; //初始链表长度为0
L->next = NULL; //链表上没有任何结点
printf("创建链表成功\n");
return L;
}
//判空操作
int list_empty(LinkListPtr L)
{
//判断逻辑
if(NULL == L)
{
printf("所给链表不合法\n");
return -1;
}
//判断指针域的内容
return L->next == NULL && L->len==0;
}
//申请结点封装数据函数
LinkListPtr node_buy(struct sockaddr_in cin)
{
//在堆区申请结点
LinkListPtr p = (LinkListPtr)malloc(sizeof(Node));
if(NULL == p)
{
printf("结点申请失败\n");
return NULL;
}
//结点申请成功,将数据封装进去
p->cin = cin;
p->next = NULL;
return p;
}
//头插
int list_insert_head(LinkListPtr L, struct sockaddr_in cin)
{
//判断逻辑
if(NULL == L)
{
printf("所给链表不合法\n");
return 0;
}
//调用申请结点封装数据
LinkListPtr p = node_buy(cin);
if(NULL==p)
{
return 0;
}
//结点已经准备好,头插逻辑
p->next = L->next;
L->next = p;
//表的变化
L->len++;
printf("插入成功\n");
return 1;
}
//遍历链表,发送数据
int list_show(LinkListPtr L, int sfd, char *buf)
{
//判断逻辑
if(NULL==L || list_empty(L))
{
printf("遍历失败\n");
return -1;
}
//遍历逻辑
printf("群发开始");
//1、定义遍历指针从第一个结点开始
LinkListPtr q = L->next;
while(q != NULL) //2、只要当前结点存在
{
//发送
if(sendto(sfd, buf, 128, 0, (struct sockaddr*)&(q->cin), sizeof(q->cin)) < 0)
{
ERR_MSG("send");
return -1;
}
q = q->next; //4、指针后移
}
printf("\n");
}
客户端代码
#define PORT 4567
#define IP "192.168.6.225"
struct msg //客户端信息结构体
{
char type;
char name[20];
char txt[128];
};
struct serinfo //线程函数传参结构体
{
int cfd;
struct sockaddr_in sin;
char name[20];
};
void *send_ser_msg(void *arg);
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket success\n");
//允许端口能被快速复用
int reuse = 1;
if(setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口被快速复用成功\n");
//填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
char buf[128] = ""; //定义字符串容器
struct sockaddr_in cin; //定义客户端地址信息结构体
socklen_t addrlen = sizeof(cin);
pthread_t tid;
struct serinfo info;
info.cfd = cfd;
info.sin = sin;
struct msg climsg;
//登录操作
char name[20];
printf("请输入用户名\n");
scanf("%s", name);
climsg.type = 'L';
strcpy(climsg.name, name);
strcpy(climsg.txt, "");
strcpy(info.name, name);
//发送
if(sendto(cfd, (struct msg*)&climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
while(1)
{
if(pthread_create(&tid, NULL, send_ser_msg, (void*)&info) != 0)
{
fprintf(stderr, "pthread_create failed__%d__\n", __LINE__);
return -1;
}
bzero(buf, sizeof(buf));
//接受信息
if(recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &addrlen) < 0)
{
ERR_MSG("recv");
return -1;
}
printf("server[%s:%d] rcvdata = %s\n", inet_ntoa(sin.sin_addr),\
ntohs(sin.sin_port), buf);
//线程分离
pthread_detach(tid);
}
if(close(cfd) < 0) //关闭文件描述符
{
ERR_MSG("close");
return -1;
}
return 0;
}
void *send_ser_msg(void *arg)
{
int cfd = ((struct serinfo*)arg)->cfd;
struct sockaddr_in sin = ((struct serinfo*)arg)->sin;
char name[20];
strcpy(name, ((struct serinfo*)arg)->name);
char buf1[128] = "";
struct msg climsg;
while(1)
{
scanf("%s", buf1);
if(strcmp((buf1), "quit") == 0)
{
climsg.type = 'Q';
strcpy(climsg.name, name);
strcpy(climsg.txt, "quit");
if(sendto(cfd, (struct msg*)&climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return NULL;
}
exit(0);
}
else
{
climsg.type = 'C';
strcpy(climsg.name, name);
strcpy(climsg.txt, buf1);
//发送
if(sendto(cfd, (struct msg*)&climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return NULL;
}
}
}
}