#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
#define PORT 8888
#define IP "192.168.31.92"
typedef void (*sighandler_t)(int);
void handler(int sig)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
exit(0);
}
int main(int argc, const char *argv[])
{
//捕获17号信号
sighandler_t s = signal(SIGCHLD, handler);
if(SIG_ERR == s)
{
perror("signal");
return -1;
}
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//服务器地址信息
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] = "";
//发送
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;
}
pid_t pid = fork();
if(pid >0)
{
while(1)
{
bzero(buf,sizeof(buf));
//发送
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = 0;
if(strcmp(buf,"") == 0)
{
printf("您没有输入内容请重新输入\n");
continue;
}
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
if(strcmp(buf,"quit")==0)
{
kill(pid,9);
wait(NULL);
break;
}
}
}
else if(pid == 0)
{
while(1)
{
bzero(buf,sizeof(buf));
//接收
if(recvfrom(sfd,buf,sizeof(buf),0,NULL,NULL) <0)
{
ERR_MSG("recvfrom");
return -1;
}
if(strcmp(buf,"quit") == 0)
{
printf("您被迫下线\n");
break;
}
printf("%s\n",buf);
}
}
else
{
ERR_MSG("fork");
return -1;
}
close(sfd);
printf("程序退出\n");
return 0;
}
服务器
//头文件
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
#define PORT 8888
#define IP "192.168.31.92"
typedef struct Node
{
struct sockaddr_in cin;//客户端信息
char name[20];//客户端名字
int len;//链表长度
struct Node *next;//指针域
}ID;
typedef struct
{
int num;
char name[20];
}res;
typedef struct
{
int sfd;
ID* info;
socklen_t addrlen;
}ssend;
ID *list_create();
int search_value(ID *L, uint32_t s_addr,in_port_t sin_port,res* resname);
int first_link(ID *L,uint32_t s_addr,in_port_t sin_port,char *name);
int send_msg(ID* L,char* buf,int sfd,size_t len,socklen_t addrlen,res resname);
int delete_user(ID *L,int num,int sfd,socklen_t addrlen);
void* callSend(void* arg);
void menu();
功能函数
#include <stdio.h>
#include "fun.h"
//创建
ID *list_create()
{
ID* L = (ID*)malloc(sizeof(ID));
if(NULL==L)
{
printf("创建失败\n");
return NULL;
}
//初始化
L->len = 0;
L->next = NULL;
return L;
}
//判断客户端是否第一次连接
int search_value(ID *L, uint32_t s_addr,in_port_t sin_port,res* resname)
{
//判断逻辑
if(NULL==L)
{
return -1;
}
//查找逻辑
//查找到客户端IP和端口一致返回1,负责返回0
ID *q = L->next;
for(int i=1;i<=L->len;i++)
{
if(q->cin.sin_addr.s_addr == s_addr && q->cin.sin_port == sin_port)
{
resname->num = i;
strcpy(resname->name,q->name);
return 1;
}
q = q->next;
}
return 0;
}
//客户端首次连接
int first_link(ID *L,uint32_t s_addr,in_port_t sin_port,char *name)
{
//判断条件
if(NULL==L)
{
printf("链表不合法\n");
return -1;
}
//申请节点
ID *p = (ID*)malloc(sizeof(ID));
if(NULL==p)
{
printf("节点申请失败\n");
return -1;
}
//存入数据
p->cin.sin_addr.s_addr = s_addr;
p->cin.sin_port = sin_port;
strcpy(p->name,name);
//完成头插
p->next = L->next;
L->next = p;
//表的变化
L->len++;
return 0;
}
//群发信息
int send_msg(ID* L,char* buf,int sfd,size_t len,socklen_t addrlen,res resname)
{
char buf_send[128] = "";
sprintf(buf_send,"%s:%s",resname.name,buf);
ID* p = L->next;
int i = 0;
for(int i=1;i<=L->len;i++)
{
if(i == resname.num)
{
p = p->next;
continue;
}
if(sendto(sfd,buf_send,strlen(buf_send),0,(struct sockaddr*)&(p->cin),addrlen) < 0)
{
ERR_MSG("sendto");
return -1;
}
p = p->next;
}
}
//用户退出,删除用户信息
int delete_user(ID *L,int num,int sfd,socklen_t addrlen)
{
char buf[128];
int i;
//判断逻辑
if(NULL==L)
{
printf("删除用户信息失败\n");
return -1;
}
ID *q = L;
for(i=1;i<num;i++)
{
q = q->next;
}
ID *p = q->next;
sprintf(buf,"%s%s%s","----系统信息:用户[",p->name,"]已退出----");
printf("%s用户退出\n",p->name);
q->next = p->next;
free(p);
L->len--;
p=L->next;
for(i=1;i<=L->len;i++)
{
if(sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&(p->cin),addrlen) < 0)
{
ERR_MSG("sendto");
return -1;
}
p = p->next;
}
return 0;
}
void* callSend(void* arg)
{
ssend* sendmsg = (ssend*)arg;
int sfd = sendmsg->sfd;
char buf[50] = "";
char buf_send[128] = "";
int i;
char c;
while(1)
{
ID* info = sendmsg->info;//链表的头指针
menu();//菜单
printf("请选择>>>>");
scanf("%c",&c);
while(getchar() != 10);
switch (c)
{
case '1':
printf("序号\tIP地址\t\t端口号\t用户名\n");
ID* q = info->next;
for(int i=1;i<=info->len;i++)
{
printf("%d\t%s\t%d\t%s\n",info->len-i+1,inet_ntoa(q->cin.sin_addr),ntohs(q->cin.sin_port),q->name);
q = q->next;
}
break;
case '2':
printf("请输入需要强制下线的序号>>>");
int num;
int i;
scanf("%d",&num);
while(getchar() != 10);
q = info->next;
for(i=1;i<=info->len;i++)
{
if((info->len-i+1) == num)
{
if(sendto(sfd,"quit",strlen("quit"),0,(struct sockaddr*)&(q->cin),sendmsg->addrlen) < 0)
{
ERR_MSG("sendto");
return NULL;
}
printf("已向[%s]发送下线指令\n",q->name);
delete_user(info,i,sfd,sendmsg->addrlen);
break;
}
q = q->next;
}
break;
case '3':
//向所有的客户端发送系统信息
q = info->next;
printf("请输入需要发送的内容>>>");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = 0;
sprintf(buf_send,"%s%s%s","----系统信息:",buf,"----");
for(i=1;i<=sendmsg->info->len;i++)
{
if(sendto(sfd,buf_send,strlen(buf_send),0,(struct sockaddr*)&(q->cin),sendmsg->addrlen) < 0)
{
ERR_MSG("sendto");
return NULL;
}
q = q->next;
}
break;
case '0':
break;
default:
printf("输入错误请重新输入\n");
}
}
}
void menu()
{
printf("----------------------------\n");
printf("----1.查看所有在线的用户----\n");
printf("----2.强制下线某用户--------\n");
printf("----3.发送系统信息----------\n");
printf("----0.退出服务器------------\n");
printf("----------------------------\n");
}
主函数
#include <stdio.h>
#include "fun.h"
int main(int argc, const char *argv[])
{
int sfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//允许端口快速复用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt\n");
return -1;
}
//填写地址结构体信息
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)//绑定
{
ERR_MSG("bind");
return -1;
}
char buf[128] = ""; //接收信息
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
int num = 0;//记录查找到第几个用户
//创建客户端信息链表
ID *info = list_create();
res resname;
//创建线程
pthread_t tid;
ssend sendmsg = {sfd,info,sizeof(cin)};
if(pthread_create(&tid,NULL,callSend,(void*)&sendmsg) < 0)
{
ERR_MSG("pthread_create");
return -1;
}
while(1)
{
bzero(buf,sizeof(buf));
//接收
if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&addrlen) < 0 )
{
ERR_MSG("rcvfrom");
return -1;
}
if((search_value(info,cin.sin_addr.s_addr,cin.sin_port,&resname))==0)
{
ID* p = info->next;
char buf_send[128] = "";
sprintf(buf_send,"%s%s%s","----系统信息:用户[",buf,"]已上线----");
for(int i=1;i<=info->len;i++)
{
sendto(sfd,buf_send,strlen(buf_send),0,(struct sockaddr*)&(p->cin),addrlen);
p=p->next;
}
printf("[%s : %d] %s已连接\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
first_link(info,cin.sin_addr.s_addr,cin.sin_port,buf);
}
else
{
printf("[%s : %d] %s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
if(strcmp(buf,"quit")==0)
{
delete_user(info,resname.num,sfd,addrlen);
}
send_msg(info,buf,sfd,strlen(buf),(socklen_t)sizeof(cin),resname);
}
}
close(sfd);
return 0;
}