项目需求:
-
如果有用户登录,其他用户可以收到这个人的登录信息
-
如果有人发送信息,其他用户可以收到这个人的群聊信息
-
如果有人下线,其他用户可以收到这个人的下线信息
-
服务器可以发送系统信息
服务器代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#define ERR_MSG(msg) {printf("line=%d\n",__LINE__);perror("msg");}
#define IP "0.0.0.0"
#define PORT 8888
typedef struct node
{
struct sockaddr_in client_information;
struct node * pro;
struct node * next;
}*client,doublelink;
//创建结构体申请函数
client list_create()
{
client p=(client)malloc(sizeof(doublelink));
if (p==NULL)
{
printf("空间申请失败\n");
return NULL;
}
p->next=NULL;
p->pro=NULL;
return p;
}
//新人头插法,将数据插入双向链表中 后进行,避免被遍历到
void new_list_insert(client head_p,client new_meber) //新人的数据结构体 头结点
{
new_meber->next=head_p->next;
new_meber->pro=head_p;
head_p->next->pro=new_meber;
head_p->next=new_meber;
return;
}
//新人上线通知
//名字字符串地址,头结点地址 先进行,不然容易遍历到自己
void new_join(char *p1,client p2,int sfd)
{
struct sockaddr_in addr;
ssize_t res;
if (p2->next==p2)
{
return ;
}
client p3=p2->next;
char frr[128];
sprintf(frr,"%s%s%s%s","****",p1,"***","*online****");
while(p3!=p2)
{
res=sendto(sfd,frr,sizeof(frr),0,(struct sockaddr*)&(p3->client_information),sizeof(addr));
if (res<0)
{
ERR_MSG("sendto");
return ;
}
p3=p3->next;
}
}
struct ifo //传参结构体
{
int fd;
client p;
};
void *recv_func(void *pp) // sfd 头结点p
{
struct ifo *p1=(struct ifo*)pp;
int sfd=p1->fd;
char arr_recv[256]="";
ssize_t arr_len=sizeof(arr_recv);
struct sockaddr_in recv_client;
socklen_t len=sizeof(recv_client);
while(1)
{
bzero(arr_recv,arr_len);
ssize_t size_recv=recvfrom(sfd,(void *)arr_recv,arr_len,0,(struct sockaddr*)&recv_client,&len);
if (size_recv<0)
{
ERR_MSG("recvfrom");
return NULL;
}
if (arr_recv[0]=='N')
{
//新人上线通知
new_join(arr_recv+1,p1->p,sfd);
//新人加入链表
client new_meber=list_create();
new_meber->client_information.sin_family=recv_client.sin_family;
new_meber->client_information.sin_port=recv_client.sin_port;
new_meber->client_information.sin_addr=recv_client.sin_addr;
new_list_insert(p1->p,new_meber);
}
else if (arr_recv[0]=='F')
{
client p3=p1->p->next;
while(p3!=p1->p)
{
if(p3->client_information.sin_port==recv_client.sin_port) //判断是不是自己
{
p3=p3->next;
continue;
}
sendto(sfd,(void *)(arr_recv+1),strlen(arr_recv)-1,0,(struct sockaddr*)&(p3->client_information),len);
p3=p3->next;
}
}
else if(arr_recv[0]=='E')
{
client p4;
client p3=p1->p->next;
while(p3!=p1->p)
{
if(p3->client_information.sin_port==recv_client.sin_port) //判断是不是自己
{
p4=p3;
p3->pro->next=p3->next;
p3->next->pro=p3->pro;
p3=p3->next;
continue;
}
sendto(sfd,(void *)(arr_recv+1),strlen(arr_recv)-1,0,(struct sockaddr*)&(p3->client_information),len);
p3=p3->next;
}
free(p4);
p4=NULL;
}
}
}
void *send_func(void *pp)
{
struct ifo *p1=(struct ifo*)pp;
struct sockaddr_in len11;
char buf[256]="";
char arr[256]="";
while(1)
{
bzero(buf,sizeof(buf));
bzero(arr,sizeof(arr));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
sprintf(arr,"%s%s%s","***system***",buf,"***system***");
client p2=p1->p->next;
while(p2!=(p1->p))
{
sendto(p1->fd,arr,sizeof(arr),0,(struct sockaddr*)&(p2->client_information),sizeof(len11));
p2=p2->next;
}
}
}
int main(int argc, const char *argv[])
{
struct ifo message;
client head_p=list_create();
if (head_p==NULL)
{
printf("头结点创建失败\n");
}
head_p->next=head_p;
head_p->pro=head_p;
message.p=head_p; //将头结点放入结构体
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if (sfd<0)
{
ERR_MSG("socket");
return -1;
}
message.fd=sfd; //将文件描述符放入结构体
//创建服务器IP结构体
struct sockaddr_in server_information;
server_information.sin_family=AF_INET;
server_information.sin_port=htons(PORT);
server_information.sin_addr.s_addr=inet_addr(IP);
//绑定服务器端口与IP
if (bind(sfd,(struct sockaddr*)&server_information,sizeof(server_information))<0)
{
ERR_MSG("bind");
return -1;
}
//创建第一个线程
pthread_t pthread_1;
if (pthread_create(&pthread_1,NULL,send_func,(void*)&message)<0)
{
ERR_MSG("pthread_create");
return -1;
}
//创建第二个线程
pthread_t pthread_2;
if (pthread_create(&pthread_2,NULL,recv_func,(void*)&message)<0)
{
ERR_MSG("pthread_create");
return -1;
}
//阻塞等待指定线程退出
pthread_join(pthread_1,NULL);
pthread_join(pthread_2,NULL);
return 0;
}
客户端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#include <arpa/inet.h>
#define ERR_MSG(msg) {printf("%d\n",__LINE__);perror("msg");};
struct inf
{
int fd;
struct sockaddr_in server_IP_PORT;
char c_name[128];
};
void *send_func(void *pp)
{
struct inf *p=(struct inf*)pp;
int sfd_1=p->fd;
struct sockaddr_in server_inf_1=p->server_IP_PORT;
char arr[128]="";
char brr[256]="";
while(1)
{
bzero(arr,sizeof(arr));
bzero(brr,sizeof(brr));
fgets(arr,sizeof(arr),stdin);
arr[strlen(arr)-1]=0;
if(strcmp(arr,"quit")==0) //判断是否退出
{
sprintf(brr,"%s%s%s%s","E",p->c_name,"****","offline"); //拼接
sendto(sfd_1,brr,sizeof(brr),0,(struct sockaddr*)&server_inf_1,sizeof(server_inf_1));
return NULL;
}
sprintf(brr,"%s%s%c%s","F",p->c_name,':',arr); //拼接
sendto(sfd_1,brr,sizeof(brr),0,(struct sockaddr*)&server_inf_1,sizeof(server_inf_1));
}
}
void *recv_func(void *pp)
{
struct inf *p=(struct inf*)pp;
int sfd_2=p->fd;
struct sockaddr_in server_inf_2=p->server_IP_PORT;
socklen_t addrlen=sizeof(server_inf_2);
char crr[128]="";
while(1)
{
bzero(crr,sizeof(crr));
recvfrom(sfd_2,crr,sizeof(crr),0,(struct sockaddr*)&server_inf_2,&addrlen);
printf("%s\n",crr);
}
}
int main(int argc, const char *argv[])
{
struct inf c_need;
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if (sfd<0)
{
ERR_MSG("socket");
return -1;
}
c_need.fd=sfd; //穿参
char name[128]="";
char crr[128]="";
printf("请输入姓名>>>>>>>");
fgets(name,sizeof(name),stdin);
name[strlen(name)-1]=0;
strcpy(c_need.c_name,name); //穿参
sprintf(crr,"%s%s","N",name);
struct sockaddr_in server_inf;
server_inf.sin_family=AF_INET;
server_inf.sin_port=htons(8888);
server_inf.sin_addr.s_addr=inet_addr("0.0.0.0");
c_need.server_IP_PORT=server_inf;
if (sendto(sfd,crr,strlen(name)+1,0,(struct sockaddr*)&server_inf,sizeof(server_inf))<0)
{
ERR_MSG("sendto");
return -1;
}
//创建线程
pthread_t pt_1;
if (pthread_create(&pt_1,NULL,send_func,(void *)&c_need)<0)
{
ERR_MSG("pthread_create");
return -1;
}
//创建线程2
pthread_t pt_2;
if (pthread_create(&pt_2,NULL,recv_func,(void *)&c_need)<0)
{
ERR_MSG("pthread_create");
return -1;
}
pthread_join(pt_1,NULL);
return 0;
}