服务器端
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <stdlib.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:%d ",__LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.0.118"//本机 IP
#define PORT 6668 //1024-49151
typedef struct Node
{
//数据域
union
{
struct sockaddr_in cin;//存储客户端信息
int len;
};
struct Node *next;
}*Linklist;
struct msg
{
char type;//L登录 C 聊天 Q下线
char name[28];
char text[128];
};
//创建头结点
Linklist create_head()
{
Linklist L=(Linklist)malloc(sizeof(struct Node));
if(L==NULL)
return NULL;
L->len=0;
L->next=NULL;
return L;
}
//创建其他节点
Linklist create_node()
{
Linklist p = (Linklist)malloc(sizeof(struct Node));
if(p == NULL)
return NULL;
p->next = NULL;
return p;
}
int do_login(struct sockaddr_in cin,Linklist L,int sfd,struct msg rcvbuf)
{
//把登录消息转发给所有人
//Linklist L = create_head();
Linklist temp = L;
while(temp->next!=NULL)
{
temp = temp->next;
if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&temp->cin,sizeof(temp->cin))<0)
{
perror("sendto");
return -1;
}
printf("登录消息发送成功\n");
}
//申请普通节点保存客户端消息
Linklist p = create_node();
p->cin = cin;
p->next = L->next;
L->next = p;
}
int do_chat(struct sockaddr_in cin ,Linklist L,int sfd,struct msg rcvbuf)
{
//把登录消息转发给所有人
//Linklist L = create_head();
Linklist temp = L;
while(temp->next!=NULL)
{
temp = temp->next;
if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&temp->cin,sizeof(temp->cin))<0)
{
perror("sendto");
return -1;
}
printf("群发消息发送成功\n");
}
}
//退出
int do_quit(struct sockaddr_in cin,Linklist L,int sfd,struct msg rcvbuf)
{
Linklist temp = L;
//判断是否是发送消息的客户端
while(temp->next != NULL)
{
//如果不是发送退出消息的客户端
if(&temp->next->cin.sin_port,&cin.sin_port != 0)
{
temp = temp->next;
if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&(temp->cin),sizeof(temp->cin))<0)
{
ERR_MSG("sendto");
return -1;
}
printf("群聊消息发送成功\n");
}
else
{
//从链表中删除
Linklist p = temp->next;
temp->next = p->next;
free(p);
p =NULL;
}
}
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;
}
//填充服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family =AF_INET;//必须填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);
ssize_t res = -1;
//Linklist L = create_head();
//接收客户端的信息结构体
struct msg rcvbuf;
//创建进程
pid_t cpid = fork();
//父进程用于接收
if(cpid>0)
{
//struct msg rcvbuf;
// 创建L用来保存客服端网络信息结构体的链表
Linklist L = create_head();
if(L == NULL)
{
printf("创建头结点失败\n");
return -1;
}
while(1)
{
bzero(rcvbuf.text,sizeof(rcvbuf.text));
bzero(rcvbuf.name,sizeof(rcvbuf.name));
res = recvfrom(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&cin,&addrlen);
if(res < 0)
{
perror("recvfrom");
}
printf("%c %s %s\n",rcvbuf.type,rcvbuf.name,rcvbuf.text);
//printf("%s : %d 信息内容:%s",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),rcvbuf.text);
switch(rcvbuf.type)
{
case 'L':
do_login(cin,L,sfd,rcvbuf);//登录
break;
case 'C':
do_chat(cin,L,sfd,rcvbuf);//群聊
break;
case 'Q':
do_quit(cin,L,sfd,rcvbuf);//客户端下线
break;
default:
printf("输入错误,请重新输入\n");
}
}
}
else if(0 == cpid)//子进程发送消息
{
rcvbuf.type='C';
while(1)
{
rcvbuf.type='C';
printf("请输入>>>");
bzero(rcvbuf.text,sizeof(rcvbuf.text));
fgets(rcvbuf.text,sizeof(rcvbuf.text),stdin);
//rcvbuf.text[strlen(rcvbuf.text)-1] = 0;
//发送数据发送给服务器
if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
printf("发送成功\n");
}
}
//关闭文件描述符
close(sfd);
return 0;
}
客户端
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:%d ",__LINE__);\
perror(msg);\
}while(0)
#define SER_IP "192.168.0.118"//本机 IP
#define SER_PORT 6668 //1024-49151
#define CLI_IP "192.168.8.61"
#define CLI_PORT 8888
struct msg
{
char type;//想做什么事,登录,群聊,下线
char name[28];//用户名
char text[128];//消息内容
};
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;//必须填AF_INET
sin.sin_port = htons(SER_PORT);//端口号的网络字节序
sin.sin_addr.s_addr= inet_addr(SER_IP);//服务器ip地址
struct msg rcvbuf;
//输入用户名
printf("请输入用户名>>>");
fgets(rcvbuf.name,sizeof(rcvbuf.name),stdin);
rcvbuf.name[strlen(rcvbuf.name)-1]=0;
//发送登录请求
rcvbuf.type = 'L';//登录请求
bzero(rcvbuf.text,sizeof(rcvbuf.text));
strcpy(rcvbuf.text,"加入群聊");
//发送数据发送给服务器
if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
printf("发送成功\n");
struct sockaddr_in rcvaddr;//存储数据包是从谁哪里来的
socklen_t addrlen = sizeof(rcvaddr);
char buf[128]="";
ssize_t res = 0;
//创建进程
pid_t cpid = fork();
//父进程用于发送
if(cpid>0)
{
while(1)
{
bzero(rcvbuf.text,sizeof(rcvbuf.text));
rcvbuf.type='C';
printf("请输入>>>");
bzero(rcvbuf.text,sizeof(rcvbuf.text));
fgets(rcvbuf.text,sizeof(rcvbuf.text),stdin);
rcvbuf.text[strlen(rcvbuf.text)-1] =0;
//判断数据是否退出
if(!strcmp(rcvbuf.text,"quit"))
{
rcvbuf.type='Q';
strcpy(rcvbuf.text,"退出群聊");
if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
kill(0,9);
break;
}
//发送数据发送给服务器
if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
printf("发送成功\n");
}
}
else if(cpid == 0)
{
while(1)
{
//接收数据
res = recvfrom(cfd,&rcvbuf,sizeof(rcvbuf),0,NULL,NULL);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
printf(" %s:内容为 %s\n",rcvbuf.name,rcvbuf.text);
}
}
//关闭文件描述符
close(cfd);
return 0;
}
现象