目录
1. 需求项目需求:1. 如果有用户登录,其他用户可以收到这个人的登录信息2. 如果有人发送信息,其他用户可以收到这个人的群聊信息3. 如果有人下线,其他用户可以收到这个人的下线信息4. 服务器可以发送系统信息
代码:
头文件myhead.h
#ifndef __MYHEAD_H__
#define __MYHEAD_H__
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
#include<wait.h>
#include<signal.h>
//打印错误信息
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__); \
perror(msg);}while(0)
typedef struct Node
{
struct sockaddr_in addr;
struct Node *next;
}node;
typedef struct Msg
{
int code;
char user[32];
char text[128];
}msg_t;
#endif
服务器server.c
#include"myhead.h"
//创建节点函数
int create_node(node **phead)
{
*phead = (node *)malloc(sizeof(node));
if(*phead == NULL)
{
printf("内存分配失败\n");
return -1;
}
(*phead)->next = NULL;
return 0;
}
//尾插
int insert_tail(node *phead,struct sockaddr_in addr)
{
if(phead == NULL)
{
printf("尾插错误\n");
return -1;
}
node *pnew = NULL;
create_node(&pnew);
pnew->addr = addr;
node *ptmp = phead;
while(ptmp->next != NULL)
{
ptmp = ptmp->next;
}
ptmp->next = pnew;
return 0;
}
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");
//填充服务器的IP地址以及端口号
if(argc < 3)
{
fprintf(stderr,"请输入端口号\n");
return -1;
}
int port = atoi(argv[2]);
if(port<1024 || port>49151)
{
fprintf(stderr,"端口号错误\n");
return -1;
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(port); //端口号
sin.sin_addr.s_addr = inet_addr(argv[1]); //IP
socklen_t serveraddr_len = sizeof(sin);
//绑定服务器的地址信息结构体
if(bind(sfd,(struct sockaddr *)&sin,serveraddr_len) == -1)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
struct sockaddr_in cin;
memset(&cin,0,sizeof(cin));
cin.sin_family = AF_INET;
cin.sin_port = htons(port); //端口号
cin.sin_addr.s_addr = inet_addr(argv[1]); //IP
socklen_t addrlen = sizeof(cin);
char name[32] = {0};
pid_t pid = fork();
msg_t msg;
msg_t msg_send;
//创建头结点
node *phead;
create_node(&phead);
phead->addr = cin;
if(pid == 0)
{
while(1)
{
//接收
memset(&msg,0,sizeof(msg));
if(recvfrom(sfd,(void *)&msg,sizeof(msg),0,(struct sockaddr *)&cin,&addrlen) < 0)
{
ERR_MSG("recvfrom");
return -1;
}
switch(msg.code){
case 1:
printf("[%s]玩家已上线\n",msg.user);
insert_tail(phead,cin);
node *q = phead->next;
while(q != NULL)
{
msg.code = 1;
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&q->addr,sizeof(q->addr)) < 0)
{
ERR_MSG("sendto");
return -1;
}
q=q->next;
}
break;
case 2:
if(strcmp("管理员",msg.user) != 0)
{
printf("[%s]:%s\n",msg.user,msg.text);
}
node *p = phead->next;
while(p != NULL)
{
msg.code = 2;
if(sendto(sfd,(void *)&msg,sizeof(msg),0,(struct sockaddr*)&p->addr,sizeof(p->addr)) < 0)
{
ERR_MSG("sendto");
return -1;
}
p=p->next;
}
break;
case 3:
printf("[%s]:退出了\n",msg.user);
node *t = phead;
node *pdel = NULL;
while(t->next != NULL)
{
msg.code = 3;
if(memcmp(&(t->next->addr),&cin,sizeof(cin)) == 0)
{
pdel = t->next;
t->next = pdel->next;
free(pdel);
}
else{
t=t->next;
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&t->addr,sizeof(t->addr)) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
}
break;
}
}
}
else if(pid > 0)
{
while(1)
{
//发送
strcpy(msg_send.user,"管理员");
memset(msg_send.text,0,32);
fgets(msg_send.text,32,stdin);
msg_send.text[strlen(msg_send.text)-1] = 0;
msg_send.code = 3;
if(sendto(sfd,&msg_send,sizeof(msg_send),0,(struct sockaddr*)&sin,serveraddr_len) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
}
else
{
perror("fork");
return -1;
}
kill(pid,SIGKILL);
wait(NULL);
//关闭套接字
close(sfd);
return 0;
}
客户端client.c
#include"myhead.h"
//创建节点函数
int create_node(node **phead)
{
*phead = (node *)malloc(sizeof(node));
if(*phead == NULL)
{
printf("内存分配失败\n");
return -1;
}
(*phead)->next = NULL;
return 0;
}
//尾插
int insert_tail(node *phead,struct sockaddr_in addr)
{
if(phead == NULL)
{
printf("尾插错误\n");
return -1;
}
node *pnew = NULL;
create_node(&pnew);
pnew->addr = addr;
node *ptmp = phead;
while(ptmp->next != NULL)
{
ptmp = ptmp->next;
}
ptmp->next = pnew;
return 0;
}
int main(int argc, const char *argv[])
{
if(argc < 3)
{
printf("请输入IP或者端口号\n");
return -1;
}
//创建套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//填充服务器信息结构体
struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(atoi(argv[2]));
sin.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t sin_len = sizeof(sin);
int bytes = 0;
char name[32] = {0};
msg_t msg;
struct sockaddr_in cin;
memset(&cin,0,sizeof(cin));
socklen_t cin_len = sizeof(cin);
//登录操作
printf("请输入登录信息:");
msg.code = 1;
memset(msg.user,0,32);
fgets(name,32,stdin);
strcpy(msg.user,name);
msg.user[strlen(msg.user)-1] = 0;
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len) < 0)
{
ERR_MSG("sendto");
return -1;
}
//创建进程
pid_t pid = fork();
if(pid == 0)
{
while(1)
{
memset(&msg,0,sizeof(msg));
//接收服务器应答
if((bytes=recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,&sin_len)) < 0)
{
ERR_MSG("recvfrom");
return -1;
}
if(strcmp(msg.user,name) == -10)
{
continue;
}else{
switch(msg.code)
{
case 1:
printf("[%s]上线了\n",msg.user);
break;
case 2:
printf("[%s]:%s\n",msg.user,msg.text);
break;
case 3:
printf("[%s]退出了\n",msg.user);
break;
}
}
}
}
else if(pid > 0)
{
while(1)
{
//在终端获取群聊
memset(msg.text,0,128);
fgets(msg.text,128,stdin);
msg.text[strlen(msg.text)-1] = 0;
if(strcmp(msg.text,"quit") == 0)
{
msg.code = 3;
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len) < 0)
{
ERR_MSG("sendto");
return -1;
}
break;
}
else{
msg.code = 2;
}
if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
}
close(sfd);
return 0;
}
执行结果: