服务器端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ERR_MSG(msg) do{\
fprintf(stderr,"%d",__LINE__);\
perror("msg");\
}while(0)
#define IP "192.168.0.112"
#define PORT 6767
struct msg
{
char flag; //'L'登录,'C'聊天,'Q'下线
char name[20];
char text[256];
};
typedef struct data
{
struct sockaddr_in cin;
char name[20];
struct data* next;
}*Linklist;
Linklist LinklistcreateData();
int do_quit(int sfd,Linklist *L,char name[20],struct sockaddr_in cin);
int do_login(int sfd,Linklist* L,struct sockaddr_in cin,char name[20]);
Linklist Linklistcreatehead();
int do_chat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256]);
int do_systemchat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256]);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//sendto有时候会没有权限 error:Permission denied
//添加权限
int on=1;
setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR|SO_BROADCAST,&on,sizeof(on));
//创建填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//绑定自身服务器端的IP和端口
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
//因为要接收数据,所以要定义一个结构体用来存储客户端的信息
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
char buf[128]="";
ssize_t res = 0;
//创建进程
pid_t cpid = fork();
//创建一个链表头
Linklist L = Linklistcreatehead();
//父进程用于接收
if(cpid > 0)
{
struct msg rcvbuf;
while(1)
{
//接收信息
if(recvfrom(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&cin,&addrlen) <0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("flag = %c,%s\n",rcvbuf.flag,rcvbuf.name);
switch(rcvbuf.flag)
{
case 'L':
do_login(sfd,&L,cin,rcvbuf.name);
break;
case 'C':
do_chat(sfd,&L,cin,rcvbuf.name,rcvbuf.text);
break;
case 'Q':
do_quit(sfd,&L,rcvbuf.name,cin);
break;
case 'S':
do_systemchat(sfd,&L,cin,rcvbuf.name,rcvbuf.text);
}
}
}
else if(0 == cpid)
{
//子进程给用户发送信息
//转发给系统,让系统发送信息给客户端
struct msg rcvbuf;
rcvbuf.flag = 'S';
bzero(rcvbuf.name,sizeof(rcvbuf.name));
strcpy(rcvbuf.name,"system");
ssize_t res = 0;
while(1)
{
scanf("%s",rcvbuf.text);
while(getchar() != 10);
/*
if(0 == strcasecmp(rcvbuf.text,"quit"))
{
rcvbuf.flag = 'Q';
}
*/
if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
/*
if(0 == strcasecmp(rcvbuf.text,"quit"))
{
close(cfd);
break;
}
*/
}
}
else if(cpid < 0)
{
ERR_MSG("fork");
return -1;
}
//关闭
close(sfd);
return 0;
}
//登录函数
int do_login(int sfd,Linklist* L,struct sockaddr_in cin,char name[20])
{
//发送用户登录消息
Linklist q = *L;
char buf[256] = "";
sprintf(buf,"用户%s已登录>>>\n",name);
while(q->next != NULL)
{
q=q->next;
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0)
{
ERR_MSG("sendto");
return -1;
}
}
//地址信息写入链表
//申请空间
Linklist p=LinklistcreateData();
//为节点填充数据
p->cin=cin;
strcpy(p->name,name);
q->next = p;
return 0; //成功返回0
}
//创建链表头
Linklist Linklistcreatehead()
{
Linklist L=(Linklist)malloc(sizeof(struct data));
if(NULL == L)
return NULL;
L->cin.sin_family = AF_INET;
L->cin.sin_port = htons(-1);
L->cin.sin_addr.s_addr = inet_addr("-1.-1.-1.-1");
L->next = NULL;
return L;
}
//创建链表节点
Linklist LinklistcreateData()
{
Linklist p =(Linklist)malloc(sizeof(struct data));
if(NULL == p)
return p;
//创建成功
p->cin.sin_family = AF_INET;
p->cin.sin_port = htons(-1);
p->cin.sin_addr.s_addr = inet_addr("-1.-1.-1.-1");
p->next = NULL;
return p;
}
//发送聊天信息
int do_chat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256])
{
char buf[256]="";
Linklist q=*L;
sprintf(buf,"%s:%s\n",name,text);
while(q->next != NULL)
{
q=q->next;
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0)
{
ERR_MSG("sendto");
return -1;
}
}
return 0;
}
//群发退出消息
int do_quit(int sfd,Linklist *L,char name[20],struct sockaddr_in cin)
{
char buf[256]="";
Linklist q=*L;
sprintf(buf,"用户:%s已退出\n",name);
while(q->next != NULL)
{
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0)
{
ERR_MSG("sendto");
return -1;
}
if(strcasecmp(q->name,name) == 0)
{
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->next->cin),sizeof(cin)) <0)
{
ERR_MSG("sendto");
return -1;
}
Linklist p=q->next;
q->next = p->next;
free(p);
p=NULL;
}
else
{
q=q->next;
}
}
return 0;
}
//发送聊天信息
int do_systemchat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256])
{
char buf[256]="";
Linklist q=*L;
sprintf(buf,"%s:%s\n",name,text);
while(q->next != NULL)
{
q=q->next;
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0)
{
ERR_MSG("sendto");
return -1;
}
}
return 0;
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#include
#define ERR_MSG(msg) do{\
fprintf(stderr,"%d",__LINE__);\
perror("msg");\
}while(0)
#define SER_IP "192.168.0.112"
#define SER_PORT 6767
#define CLI_IP "192.168.0.111"
#define CLI_PORT 7777
struct msg
{
char flag;
char name[20];
char text[256];
};
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET,SOCK_DGRAM,0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
//创建填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
struct sockaddr_in rcvaddr;
socklen_t addrlen = sizeof(rcvaddr);
//填充协议
struct msg rcvbuf;
rcvbuf.flag = 'L';
printf("请输入你的名字>>>\n");
scanf("%s",rcvbuf.name);
while(getchar() != 10);
ssize_t res = 0;
//发送登录请求
if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
printf("发送成功\n");
char buf[256]="";
//创建子进程,实现随时收发
pid_t cpid=fork();
//父进程发送消息
if(cpid > 0)
{
rcvbuf.flag = 'C';
while(1)
{
scanf("%s",rcvbuf.text);
if(0 == strcasecmp(rcvbuf.text,"quit"))
{
rcvbuf.flag = 'Q';
}
if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
if(0 == strcasecmp(rcvbuf.text,"quit"))
{
close(cfd);
break;
}
}
}
//子进程接收消息
else if(0 == cpid)
{
while(1)
{
bzero(buf,sizeof(buf));
if(recvfrom(cfd,&buf,sizeof(buf),0,NULL,NULL) < 0)
{
ERR_MSG("fork");
return -1;
}
if(1 == getppid())
{
printf("父进程退出,子进程准备退出>>>\n");
break;
}
printf("接收信息成功\n");
printf("%s",buf);
}
}
if(cpid < 0)
{
ERR_MSG("fork");
return -1;
}
//关闭
close(cfd);
close(cpid);
return 0;
}
运行结果:
PS:接收信息成功和父进程退出两句话是测试用的,可以注掉