功能:
有新用户登录,其他在线的用户可以收到登录信息
有用户群聊,其他在线的用户可以收到群聊信息
有用户退出,其他在线的用户可以收到退出信息
服务器可以发送系统信息
执行思路
udp客户端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d\n", __LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.2.108" //填你们自己的本机IP ifconfig查看
#define PORT 6666 //1024~49151
//创建数据结构体
typedef struct {
char type;
char name[20];
char text[128];
}__attribute__((packed)) msg;
msg inform; //存储发送信息
msg inform_rec; //存储接收信息
struct sockaddr_in cin; //存储要发送的服务器信息
pthread_t pth_rec;
pthread_t pth_snd;
//发送线程函数
void *pthread_snd(void *p);
//接收线程函数
void *pthread_rec(void *p);
struct sockaddr_in rcvAddr; //存储接收到的数据包的地址信息
socklen_t addrlen = sizeof(rcvAddr); //接收到的数据包的地址信息的大小
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//绑定客户端自身的地址信息结构体-->非必须绑定,建议不写
cin.sin_family = AF_INET;
cin.sin_port = htons(PORT);
cin.sin_addr.s_addr = inet_addr(IP);
char buf[128] = "";
ssize_t res = 0;
inform.type = 'L';
printf("请输入用户名>>>");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = 0;
strcpy(inform.name,buf);
//发送登录消息
if(sendto(sfd,&inform,sizeof(inform),0,(struct sockaddr *)&cin,sizeof(cin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//创建发送线程
if(pthread_create(&pth_snd,NULL,pthread_snd,&sfd) < 0)
{
ERR_MSG("pthread_create");
return -1;
}
//创建接收线程
if(pthread_create(&pth_rec,NULL,pthread_rec,&sfd) < 0)
{
ERR_MSG("pthread_create");
return -1;
}
//阻塞线程
pthread_join(pth_rec,NULL);
pthread_join(pth_snd,NULL);
//关闭套接字
close(sfd);
return 0;
}
//发送线程
void *pthread_snd(void *p)
{
int sfd = *(int *)p;
ssize_t res = 0;
while(1)
{
bzero(inform.text, sizeof(inform.text));
inform.type = 'C';
fgets(inform.text, sizeof(inform.text), stdin);
inform.text[strlen(inform.text)-1] = 0;
if(strcmp(inform.text,"quit") == 0)
{
inform.type = 'Q';
}
if(sendto(sfd, &inform, sizeof(inform), 0, (struct sockaddr*)&cin, sizeof(cin)) < 0)
{
ERR_MSG("sendto");
return NULL;
}
if(strcmp(inform.text,"quit") == 0)
{
pthread_cancel(pth_rec);
pthread_exit(NULL);
}
printf("sendto success\n");
}
}
void *pthread_rec(void *p)
{
char c = 0;
int sfd = *(int *)p;
ssize_t res = 0;
while(1)
{
//接收
bzero(&inform_rec, sizeof(inform_rec));
res = recvfrom(sfd, &inform_rec, sizeof(inform_rec), 0, (struct sockaddr*)&rcvAddr, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return NULL;
}
c = inform_rec.type;
if('L' == c)
{
printf("-----[%s]已上线------\n",inform_rec.name);
}
else if('C' == c)
{
printf("[%s]:%s\n",inform_rec.name,inform_rec.text);
}
else if('Q' == c)
{
printf("-----[%s]已下线-----\n",inform_rec.name);
}
}
}
upd服务器代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d\n", __LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.2.108" //填你们自己的本机IP ifconfig查看
#define PORT 6666 //1024~49151
//创建数据结构体
typedef struct {
char type; //存储协议
char name[20]; //用户名
char text[128]; //数据
} __attribute__((packed)) msg;
//定义用户信息结构体
typedef struct Node{
union{
int len; //头结点数据域
struct sockaddr_in data; //普通节点数据域
};
struct Node *next; //指针域
}Linklist;
//链表头结点
Linklist *L;
msg inform; //存储接收信息
msg inform_sen; //存储发送信息
//存储用户信息结构体
struct sockaddr_in cin;
//用户信息结构体的大小
socklen_t addr = sizeof(cin);
//报式套接字文件描述符
int sfd = 0;
//登录遍历链表函数
void list_show(Linklist *L,msg p);
//申请节点封装数据函数
Linklist *buy_node(struct sockaddr_in e);
//尾插函数
int list_insert_tail(Linklist *L,struct sockaddr_in e);
//发送信息遍历链表
void list_show1(Linklist *L,msg p,struct sockaddr_in sin);
//按值进行查找函数
int list_search_value(Linklist *L,struct sockaddr_in e);
//任意位置删除
int list_delete_pos(Linklist *L,int pos);
//用户下线遍历链表
void list_show2(Linklist *L,msg p);
//创建链表
Linklist *list_create();
//判空函数
int list_empty(Linklist *L);
//接收线程函数
void *pthread_rec(void *p);
//发送线程函数
void *pthread_sen(void *p);
//按位置查找返回结点
Linklist *find_node(Linklist *L,int pos);
int main(int argc, const char *argv[])
{
//创建链表
L = list_create();
if(NULL == L)
{
printf("%d:创建失败\n",__LINE__);
return -1;
}
//创建报式套接字
sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//允许端口快速重用--->允许程序退出后,端口号能被新的进程快速覆盖
//必须放在bind之前
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) <0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口快速重用成功\n");
//填充服务器自身的地址信息结构体
cin.sin_family = AF_INET;
cin.sin_port = htons(PORT);
cin.sin_addr.s_addr = inet_addr(IP);
//绑定IP和端口
if(bind(sfd,(struct sockaddr *)&cin,sizeof(cin)) < 0)
{
ERR_MSG("bind");
return -1;
}
pthread_t pth_rec;
pthread_t pth_sen;
//创建接收线程
if(pthread_create(&pth_rec,NULL,pthread_rec,&sfd) < 0)
{
ERR_MSG("pthread_create");
return -1;
}
//创建发送线程
if(pthread_create(&pth_sen,NULL,pthread_sen,&sfd) < 0)
{
ERR_MSG("pthread_create");
return -1;
}
//阻塞等待线程退出
pthread_join(pth_rec,NULL);
pthread_join(pth_sen,NULL);
//关闭文件描述符
close(sfd);
return 0;
}
//接收线程
void *pthread_rec(void *p)
{
int sfd = *(int *)p;
ssize_t res = 0;
char c = 0;
int a = 0;
while(1)
{
res = recvfrom(sfd,&inform,sizeof(inform),0,(struct sockaddr *)&cin,&addr);
if(res < 0)
{
ERR_MSG("recvfrom");
return NULL;
}
c = inform.type;
switch(c)
{
case 'L':
//打印信息
printf("---[%s]上线---\n",inform.name);
//遍历函数发送数据
list_show(L,inform);
//添加结点
list_insert_tail(L,cin);
break;
case 'C':
//打印信息
printf("---[%s]:%s---\n",inform.name,inform.text);
//遍历转发数据
list_show1(L,inform,cin);
break;
case 'Q':
//打印信息
printf("---[%s]下线---\n",inform.name);
//按值查找返回位置
a = list_search_value(L,cin);
//任意位置删除
list_delete_pos(L,a);
//遍历输出数据
list_show2(L,inform);
break;
}
}
}
//发送线程
void *pthread_sen(void *p)
{
int sfd = *(int *)p;
char str[128] = "";
inform_sen.type = 'C';
strcpy(inform_sen.name,"system");
while(1)
{
bzero(str,sizeof(str));
fgets(str,sizeof(str),stdin);
str[strlen(str)-1] = 0;
strcpy(inform_sen.text,str);
list_show2(L,inform_sen);
}
}
//遍历链表
void list_show(Linklist *L,msg p)
{
//判断逻辑
if(NULL == L || list_empty(L))
{
//puts("遍历失败");
return;
}
//遍历逻辑
Linklist *q = L->next; //定义指针指向第一个结点
while(q != NULL)
{
if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
{
ERR_MSG("sendto");
return ;
}
q = q->next;
}
}
//申请节点封装数据函数
Linklist *buy_node(struct sockaddr_in e)
{
//在堆区申请一个结点的空间
Linklist *p = (Linklist *)malloc(sizeof(Linklist));
if(NULL == p)
{
puts("空间申请失败");
return NULL;
}
//将数据封装到结点的数据域中
p->data = e;
p->next = NULL;
return p;
}
//尾插函数
int list_insert_tail(Linklist *L,struct sockaddr_in e)
{
//判断逻辑
if(NULL == L)
{
puts("所给链表不合法");
return 0;
}
//申请节点封装数据
Linklist *p = buy_node(e);
//定义遍历指针,指向最后一个结点
Linklist *q = L;
while(q->next != NULL)
{
q = q->next;
}
//尾插操作
p->next = q->next;
q->next = p;
//表长变化
L->len++;
return 1;
}
//遍历链表
void list_show1(Linklist *L,msg p,struct sockaddr_in sin)
{
//判断逻辑
if(NULL == L || list_empty(L))
{
//puts("遍历失败");
return;
}
//遍历逻辑
Linklist *q = L->next; //定义指针指向第一个结点
while(q != NULL)
{
//printf("%c\t",q->data);
if(memcmp(&q->data,&sin,sizeof(cin)) != 0)
{
if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
{
ERR_MSG("sendto");
return ;
}
}
q = q->next;
}
}
//按值进行查找函数
int list_search_value(Linklist *L,struct sockaddr_in e){
//判断逻辑
if(NULL == L || list_empty(L)){
puts("查找失败");
return -1;
}
//查找逻辑
Linklist *q = L->next; //从第一个结点出发
for(int i=1;i<=L->len;i++){
if(memcmp(&q->data,&e,sizeof(cin)) == 0){
return i;
}
q = q->next; //继续判断下一个结点
}
return 0;
}
//任意位置删除
int list_delete_pos(Linklist *L,int pos){
//判断逻辑
if(NULL == L || list_empty(L) || pos<1 || pos>L->len){
puts("删除失败");
return 0;
}
//找到要删除结点的前驱
Linklist *q = find_node(L,pos-1);
if(NULL == q){
puts("删除失败");
return 0;
}
//删除操作
Linklist *p = q->next;
q->next = p->next;
free(p);
p = NULL;
//表长变化
L->len--;
puts("删除成功");
return 1;
}
//遍历链表
void list_show2(Linklist *L,msg p)
{
//判断逻辑
if(NULL == L || list_empty(L))
{
//puts("遍历失败");
return;
}
//遍历逻辑
Linklist *q = L->next; //定义指针指向第一个结点
while(q != NULL)
{
if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
{
ERR_MSG("sendto");
return ;
}
q = q->next;
}
}
//创建链表
Linklist *list_create(){
Linklist *L = (Linklist *)malloc(sizeof(Linklist));
if(NULL == L){
puts("创建失败");
return NULL;
}
//创建成功进行初始化
L->len = 0;
L->next = NULL;
puts("创建成功");
return L;
}
//判空函数
int list_empty(Linklist *L)
{
return L->next == NULL;
}
//按位置查找返回结点
Linklist *find_node(Linklist *L,int pos){
//判断逻辑
if(pos<0 || pos>L->len){
puts("查找失败");
return NULL;
}
//查找逻辑
//定义指针从头结点出发
Linklist *q=L;
for(int i=0;i<pos;i++){
q = q->next;
}
return q; //将找到的结点返回
}
执行结果