1,服务器代码:
(1)头文件
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
//打印错误信息宏函数
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
//定义用户结构体
typedef struct node{
struct sockaddr_in cin; //存储接收到的数据包来自哪里
socklen_t addrlen;
char name[20];
int stop_speaking;
int size;
struct node* next;
}CLI;
//创建用户结构体信息链表
CLI* create();
//判断接收到是不是新成员
int judge(CLI *H,CLI*q);
//尾插法插入新用户
int insert_cli(int sfd,CLI *H,CLI**p,char *name);
//发送信息给其他成员
int senmsg(int sfd,CLI* H,CLI*q,char *msg);
//删除指定成员
int deleat_num(int sfd,CLI *H,CLI**q);
//显示现有成员
void show(CLI*H);
//禁言
int stop_speak(int sfd,CLI *H,char*name);
//踢出
int get_out(int sfd,CLI*H,char *name);
//取消禁言
int stat_speak(int sfd,CLI *H,char*name);
#endif
(2)功能代码
#include "head.h"
//创建用户结构体信息链表
CLI* create()
{
CLI *H=(CLI*)malloc(sizeof(CLI));
if(NULL==H)
{
puts("创建失败\n");
return NULL;
}
//初始化
H->next=NULL;
H->size=0;
H->stop_speaking=0;
strcpy(H->name,"head");
puts("创建链表成功");
return H;
}
//判断接收到是不是新成员
int judge(CLI *H,CLI*q)
{
//判断逻辑
if(NULL==H)
{
printf("链表不存在\n");
return -1;
}
//判断是不是空表
if(0==H->size)
{
return 1;
}
//循环遍历是不是新成员
CLI*p=H->next;
while(p!=NULL)
{
if(p->cin.sin_port == q->cin.sin_port)
{
return 0;
}
p=p->next;
}
return 1;
}
//尾插法插入新用户
int insert_cli(int sfd,CLI *H,CLI**new,char *name)
{
CLI *p=H;
//偏移指针
while(p->next!=NULL)
{
p=p->next;
}
//完成尾插
p->next=*new;
strcpy((*new)->name,name);
(*new)->stop_speaking=0;
//表的变化
H->size++;
//发送登录信息给其他成员
strcat(name,"登录成功");
p=H->next;
while(p != *new)
{
if(sendto(sfd,name,strlen(name),0,(struct sockaddr*)&(p->cin),p->addrlen)<0)
{
ERR_MSG("sendto");
return -1;
}
p=p->next;
}
return 0;
}
//发送信息给其他成员
int senmsg(int sfd,CLI* H,CLI*q,char *msg)
{
//封装成员名在要发送的内容前
char arr[200]="";
CLI*find=H->next;
while(find!=NULL)
{
if((find->cin).sin_port == (q->cin).sin_port)
{
if(find->stop_speaking==1)
{
return -1;
}
strcpy(arr,find->name);
strcat(arr,":");
strcat(arr,msg);
}
find=find->next;
}
CLI*p=H->next;
//循环发送信息
while(p!=NULL)
{
if((p->cin).sin_port == (q->cin).sin_port)
{
p=p->next;
continue;
}
if(sendto(sfd,arr,strlen(arr),0,(struct sockaddr*)&(p->cin),p->addrlen)<0)
{
ERR_MSG("sendto");
return -1;
}
p=p->next;
}
return 0;
}
//删除指定成员
int deleat_num(int sfd, CLI *H,CLI**qe)
{
CLI *q=*qe;
char arr[100]="";
//找到对应要删除的节点,封装退出字符串
CLI*find=H;
while(find->next!=NULL)
{
if(find->next->cin.sin_port == q->cin.sin_port)
{
strcpy(arr,find->next->name);
strcat(arr,"退出");
break;
}
}
//删除指定成员
CLI*temp=find->next;
find->next=temp->next;
free(temp);
temp=NULL;
//发送退出信息给其他成员
CLI *p=H->next;
while(p!=NULL)
{
if(sendto(sfd,arr,strlen(arr),0,(struct sockaddr*)&(p->cin),p->addrlen)<0)
{
ERR_MSG("sendto");
return -1;
}
p=p->next;
}
return 0;
}
//显示现有成员
void show(CLI*H)
{
CLI*p=H->next;
while(p!=NULL)
{
printf("遍历:%s\n",p->name);
p=p->next;
}
return;
}
//禁言
int stop_speak(int sfd,CLI *H,char*name)
{
CLI*p=H;
while(p!=NULL)
{
if(strcmp(p->name,name)==0)
{
p->stop_speaking=1;
char arr[20]="stoptalking";
sendto(sfd,arr,sizeof(arr),0,(struct sockaddr*)&(p->cin),p->addrlen);
}
p=p->next;
}
return 0;
}
//取消禁言
int stat_speak(int sfd,CLI *H,char*name)
{
CLI*p=H;
while(p!=NULL)
{
if(strcmp(p->name,name)==0)
{
p->stop_speaking=0;
char arr[20]="stattalking";
sendto(sfd,arr,sizeof(arr),0,(struct sockaddr*)&(p->cin),p->addrlen);
}
p=p->next;
}
return 0;
}
//踢出
int get_out(int sfd,CLI*H,char *name)
{
CLI*p=H;
while(p!=NULL)
{
if(strcmp(p->name,name)==0)
{
//通知用户端退出
char arr[200]="quit";
sendto(sfd,arr,sizeof(arr),0,(struct sockaddr*)&(p->cin),p->addrlen);
//找到对应要删除的节点,封装退出字符串
strcpy(arr,name);
strcat(arr,"退出");
//删除指定成员
CLI *find=H;
while(find->next!=NULL)
{
if(find->next==p) break;
}
CLI*temp=find->next;
find->next=temp->next;
free(temp);
temp=NULL;
//发送退出信息给其他成员
CLI *p1=H->next;
while(p1!=NULL)
{
if(sendto(sfd,arr,strlen(arr),0,(struct sockaddr*)&(p1->cin),p1->addrlen)<0)
{
ERR_MSG("sendto");
return -1;
}
p1=p1->next;
}
break;
}
p=p->next;
}
return 0;
}
(3)服务器主代码
#include "head.h"
int sfd=0;
void* sys_msg(void*arg)
{
pthread_detach(pthread_self());
CLI *H=(CLI*)arg;
int num;
while(1)
{
printf("************管理员************\n");
printf("** 1,发送系统信息 **\n");
printf("** 2,禁言 **\n");
printf("** 3,踢出 **\n");
printf("** 4,取消禁言 **\n");
printf("******************************\n");
printf("请输入选项:");
scanf("%d",&num);
getchar();
switch(num)
{
case 1:
{
printf("请输入系统消息:");
char buf[500]="";
char arr[128]="";
//系统信息输入
bzero(arr,sizeof(arr));
bzero(buf,sizeof(buf));
strcpy(buf,"系统消息:");
fgets(arr,sizeof(arr),stdin);
arr[strlen(arr)-1]=0;
strcat(buf,arr);
CLI *p=H->next;
//系统信息发送
while(p!=NULL)
{
if(sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&(p->cin),p->addrlen)<0)
{
ERR_MSG("sendto");
return NULL;
}
p=p->next;
}
}
break;
case 2:
{
char name[20];
printf("请输入要禁言的名字:");
fgets(name,sizeof(name),stdin);
name[strlen(name)-1]=0;
stop_speak(sfd,H,name);
}
break;
case 3:
{
char name[20];
printf("请输入要踢出的名字:");
fgets(name,sizeof(name),stdin);
name[strlen(name)-1]=0;
get_out(sfd,H,name);
}
break;
case 4:
{
char name[20];
printf("请输入要取消禁言的名字:");
fgets(name,sizeof(name),stdin);
name[strlen(name)-1]=0;
stat_speak(sfd,H,name);
}
break;
default:printf("输入错误,请重新输入\n"); break;
}
}
}
int main(int argc, const char *argv[])
{
//创建套接字
sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//填充服务器的IP地址以及端口号
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr("192.168.31.107");
//绑定服务器的地址信息结构体
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
//创建用户结构体信息链表
//头节点并初始化
CLI *H=create();
//创建发送系统信息线程
pthread_t tid;
if(pthread_create(&tid,NULL,sys_msg,(void*)H)!=0)
{
ERR_MSG("pthread_create");
return -1;
}
//接收用户发送的信息
char buf[128]="";
//接收
while(1)
{
bzero(buf,sizeof(buf));
//申请一个新的用户信息节点
CLI *q=(CLI*)malloc(sizeof(CLI));
q->next=NULL;
q->addrlen=sizeof(q->cin);
q->size=0;
//接收信息
if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),&(q->addrlen))<0)
{
ERR_MSG("recvfrom");
return -1;
}
//判断是不是新用户
if(1==judge(H,q)) //是新用户,链表新增,并发信息给
{
insert_cli(sfd,H,&q,buf);
}else
{
if(strcmp(buf,"quit")==0)
{
deleat_num(sfd,H,&q);
}else{
senmsg(sfd,H,q,buf);
}
}
}
close(sfd);
return 0;
}
2,用户代码
用户代码示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
//打印错误信息宏函数
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
int sfd=0;
void* recv1(void* reg)
{
pthread_detach(pthread_self());
struct sockaddr_in cin;
char buf[300]="";
socklen_t addrlen=sizeof(cin);
while(1)
{
bzero(buf,sizeof(buf));
if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&addrlen)<0)
{
ERR_MSG("recvfrom");
return NULL;
}
if(strcmp(buf,"quit")==0)
{
printf("你被踢出群聊\n");
exit(0);
}else if(strcmp(buf,"stoptalking")==0)
{
printf("你已被禁言\n");
}else if(strcmp(buf,"stattalking")==0)
{
printf("你被取消禁言\n");
}
else{
printf("%s\n",buf);
}
}
}
int main(int argc, const char *argv[])
{
//创建套接字
sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//客户端
struct sockaddr_in cin;
cin.sin_family = AF_INET;
cin.sin_port = htons(2222);
cin.sin_addr.s_addr = inet_addr("192.168.31.107");
//绑定用户端的地址信息结构体
if(bind(sfd,(struct sockaddr*)&cin,sizeof(cin))<0)
{
ERR_MSG("bind");
return -1;
}
//服务器
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr("192.168.31.107");
//连接服务器,并发送自己的名字
char buf[128]="";
printf("请输入网名:");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
pthread_t tid;
if(pthread_create(&tid,NULL,recv1,(void*)&sin)<0)
{
ERR_MSG("pthread_create");
return -1;
}
while(1)
{
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin));
if(strcmp(buf,"quit")==0)
{
exit(0);
}
}
close(sfd);
return 0;
}
运行示例: