cli.c
#include"udp.h"
#define ERR_MSG(msg) do{\
fprintf(stderr, "__%d__:\n", __LINE__); \
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
pid_t pid=0;
msg_t msg;//创建发送用户信息
int cfd=socket(AF_INET,SOCK_DGRAM,0);
if(cfd<0)
{
ERR_MSG("套接字创建失败");
return -1;
}
struct sockaddr_in cin;
memset(&cin,0,sizeof(cin));
cin.sin_family =AF_INET;
cin.sin_port =htons(3696);
cin.sin_addr.s_addr=inet_addr("192.168.114.146");
socklen_t caddr_len=sizeof(cin);
//输入用户的姓名开始操作
msg.ch='D';
printf("请输入你的网名QVQ >>>>");
fgets(msg.name,sizeof(msg.name),stdin);
msg.name[strlen(msg.name)-1]='\0';
//给服务器发送用户已经登录的操作
sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,caddr_len);
//创建子进程,子进程接收,父进程发送
pid=fork();
if(pid==-1)
{
ERR_MSG("客户端创建进程失败");
}else if(pid==0)
{
//子进程
while(1)
{
memset(&msg,0,sizeof(msg));
//接受服务器的信息
if(recvfrom(cfd,&msg,sizeof(msg),0,NULL,NULL)==-1)
{
ERR_MSG("recvfrom");
}
printf("[%s]>>>>(%s)\n",msg.name,msg.text);
}
}else
{
//写入聊天的内容
while(1)
{
memset(msg.text,0,sizeof(msg.text));
fgets(msg.text,sizeof(msg.text),stdin);
msg.text[strlen(msg.text)-1]='\0';
if(strcmp(msg.text,"quit")==0)
{
msg.ch='Q';
}else{
msg.ch='L';
}
//将写好的内容发给服务器
if(sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,caddr_len)==-1)
{
ERR_MSG("聊天内容发送失败");
}
//当识别停止时结束进程
if(strcmp(msg.text,"quit")==0)
{
kill(pid,SIGKILL);
close(cfd);
exit(EXIT_SUCCESS);
}
}
}
return 0;
}
ser.c
#include "udp.h"
#define ERR_MSG(msg) do{\
fprintf(stderr, "__%d__:\n", __LINE__); \
perror(msg);\
}while(0)
//聊天用结构体
int main(int argc, const char *argv[])
{
//进程
pid_t pid;
msg_t msg;//进行各种操作
msg_t faso;//用来发送信息
//创建报式套接字
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("创建服务器套接字失败");
return -1;
}
//填充接收方的信息给bind用
struct sockaddr_in sin;
sin.sin_family =AF_INET;
sin.sin_port =htons(3696);
sin.sin_addr.s_addr=inet_addr("192.168.114.146");
socklen_t sadd_len=sizeof(sin);
//绑定信息结构体
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("套接字绑定失败");
return -1;
}
struct sockaddr_in cin;//存储发送方的信息
cin.sin_family=AF_INET;
socklen_t cadd_len=sizeof(cin);
//创建一个进程
pid=fork();
if(pid==-1)
{
ERR_MSG("服务器进程创建失败");
}
else if(pid==0)
{
//子进程
//创建一个单链表保存网络信息结构
user_t *head;
creat_head(&head);
memset(&msg,0,sizeof(msg));
while(1)
{
if(recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&cadd_len)==-1)
{
ERR_MSG("接收客户端信息失败");
}
switch(msg.ch)
{
case 'D':
//用户登录
input_addr(head,msg,sfd,cin,cadd_len);
break;
case 'L':
//用户聊天
test_addr(head,msg,sfd,cin,cadd_len);
break;
case 'Q':
//用户退出
tuichu_addr(head,msg,sfd,cin,cadd_len);
break;
}
}
}
else
{
while(1)
{
//发系统消息
memset(&faso,0,sizeof(faso));
fgets(faso.text,sizeof(faso.text),stdin);
faso.text[strlen(faso.text)-1]='\0';
faso.ch='L';
sprintf(faso.name,"%s","系统消息");
if(sendto(sfd,&faso,sizeof(faso),0,(struct sockaddr*)&sin,sadd_len)==-1)
{
ERR_MSG("发送系统消息失败");
}
}
}
return 0;
}
upd.c
#include "udp.h"
#define ERR_MSG(msg) do{\
fprintf(stderr, "__%d__:\n", __LINE__); \
perror(msg);\
}while(0)
//创建一个单链表头
int creat_head(user_t **head)
{
if(head==NULL)
{
printf("传送错误,请检查\n");
return -1;
}
(*head)=(userptr)malloc(sizeof(user_t));
(*head)->next=NULL;
return 0;
}
//记录用户登录的信息
int input_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len)
{
if(head==NULL)
{
printf("传送错误,请检查\n");
return -1;
}
//把用户信息发给所有人
snprintf(msg.text,sizeof(msg.text),"[%s]%s",msg.name,"来喽,快去找他聊天吧!");
//用来记录头的地址
user_t *user_head=head;
while(user_head->next!=NULL)
{
user_head=user_head->next;
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&user_head->addr,cliaddr_len)==-1)
{
ERR_MSG("用户信息发送给所有人失败");
}
}
//创建一个新的节点,并且把用户信息放入单链表
user_t *temp=NULL;
creat_head(&temp);
temp->addr=cliaddr;
//用头插法将用户插入链表
temp->next=head->next;
head->next=temp;
return 0;
}
//发送信息给聊天室的所有人除了自己
int test_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len)
{
if(head==NULL)
{
printf("传送错误,请检查\n");
return -1;
}
//将接受到的信息发送给除了自己以外的所有人
user_t *user_head=head;
while(user_head->next!=NULL)
{
user_head=user_head->next;
if(0!=memcmp(&(user_head->addr),&cliaddr,sizeof(cliaddr)))
{
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&user_head->addr,cliaddr_len)==-1)
{
ERR_MSG("信息发送给所有人失败");
}
}
}
return 0;
}
//退出登录并且把消息发送给所有人
int tuichu_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len)
{
if(head==NULL)
{
printf("传输错误,请检查\n");
return -1;
}
snprintf(msg.text,sizeof(msg.text),"%s%s",msg.name,"优雅的走喽~~~");
user_t *user_head=head;
user_t *pdel=NULL;
while(user_head->next!=NULL)
{
if(0==memcmp(&(user_head->next->addr),&cliaddr,sizeof(cliaddr)))
{
pdel=user_head->next;
user_head->next=pdel->next;
free(pdel);
pdel=NULL;
}
else
{
user_head=user_head->next;
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&user_head->addr,cliaddr_len)==-1)
{
ERR_MSG("将退出信息告诉所有人失败");
}
}
}
return 0;
}
udp.h
#ifndef _UDP_H
#define _UDP_H
#include<myhead.h>
#define N 512
#define ERR_MSG(msg) do{\
fprintf(stderr, "__%d__:\n", __LINE__); \
perror(msg);\
}while(0)
#define SER_PORT 3696//服务器的端口号
#define GRP_IP "224.1.2.3"//组播IP
#define LOL_IP "192.168.114.140"//本机IP
//聊天用结构体
typedef struct Msg
{
char ch;//L聊天,q退出,登入d
char name[128];//储存用户名字
char text[N];//储存聊天内容
}msg_t;
typedef struct User//存储用户信息
{
struct sockaddr_in addr;
struct User *next;
}user_t,*userptr;
//创建一个单链表头
int creat_head(user_t **head);
//记录用户登录的信息
int input_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len);
//发送信息给聊天室的所有人除了自己
int test_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len);
//退出登录并且把消息发送给所有人
int tuichu_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len);
#endif