客户端
#include <myhead.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d %s\t%s", __LINE__, __FILE__, __func__);\
perror(msg);\
}while(0)
#define IP "192.168.2.54" //ifconfig出来的本机IP
#define PORT 6666 //1024~49151,网络字节序
typedef struct
{
char type; //L登录C群聊Q下载
char name[20]; //用户名
char text[128]; //群聊内容
}msg_t;
void handler(int sig)
{
//回收子进程
wait(0);
kill(getpid(),SIGINT);
}
int func_chat(int sfd, msg_t msg, struct sockaddr_in sin);
int func_recv(int sfd, msg_t msg,char name_cli[20], struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
//捕获17) SIGCHLD信号
if(signal(SIGCHLD, handler) ==SIG_ERR)
{
ERR_MSG("signal");
return -1;
}
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("create socket success sfd = %d __%d__\n", sfd, __LINE__);
//填充服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//定义信息交流结构体
msg_t msg;
msg.type = 'L';
printf("请输入用户名>>>>");
fgets(msg.name, sizeof(msg.name), stdin);
msg.name[strlen(msg.name)-1] = '\0';
char name_cli[20] = "";
strcpy(name_cli, msg.name);
//发送登录信息
if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto apply");
return -1;
}
pid_t pid = fork();
if(pid < 0)
{
ERR_MSG("fork");
return -1;
}
else if(pid == 0)
{
//子进程
//功能:群聊 下线
func_chat(sfd, msg, sin);
//退出子进程
exit(0);
}
//父进程
//功能:接收聊天内容
func_recv(sfd, msg,name_cli, sin);
//杀死子进程
kill(pid, SIGKILL);
//等待回收子进程
wait(NULL);
//关闭套接字
close(sfd);
return 0;
}
int func_chat(int sfd, msg_t msg, struct sockaddr_in sin)
{
while(1)
{
//从终端获取数据
printf("请输入>>>");
fgets(msg.text, sizeof(msg.text), stdin);
msg.text[strlen(msg.text)-1] = '\0';
//退出群聊
if(strcmp(msg.text, "quit") == 0)
{
msg.type = 'Q';
//通知服务器,该用户下线
if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto quit");
return -1;
}
break;
}
//群聊
msg.type = 'C';
//向服务器发送数据
if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto data");
return -1;
}
}
return 0;
}
int func_recv(int sfd, msg_t msg, char name_cli[20], struct sockaddr_in sin)
{
while(1)
{
socklen_t addrlen = sizeof(sin);
if(recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, &addrlen) < 0)
{
ERR_MSG("recvfrom");
return -1;
}
if(strcmp(msg.name, name_cli) != 0)
printf("[%s]:%s\n",msg.name, msg.text);
}
return 0;
}
服务器
#include <myhead.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d %s\t%s", __LINE__, __FILE__, __func__);\
perror(msg);\
}while(0)
#define IP "192.168.2.54" //ifconfig出来的本机IP
#define PORT 6666 //1024~49151,网络字节序
int iii=0;
//信息交流结构体
typedef struct
{
char type; //L登录C群聊Q下载
char name[20]; //用户名
char text[128]; //群聊内容
}msg_t;
//存储用户地址链表
typedef struct ADDR
{
//数据域
union
{
//头结点的数据域:链表长度
int len;
//其他节点的数据域:数据元素
struct sockaddr_in cin;
};
//指针域:下一个节点的地址
struct ADDR *next;
}*addrlist;
//需要传入到线程中数据
struct climsg
{
int sfd;
addrlist list;
};
addrlist create(int flag);
int func_delete(int sfd, msg_t msg, addrlist list, struct sockaddr_in rcvaddr);
int func_send(addrlist list, int sfd, msg_t msg);
int func_log(addrlist list, struct sockaddr_in rcvaddr);
void* deal_cli_msg(void* arg);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("create socket success sfd = %d __%d__\n", sfd, __LINE__);
//填充服务器的地址信息结构体
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;
}
//存储数据包是从哪里来的
struct sockaddr_in rcvaddr;
socklen_t addrlen_rcv = sizeof(rcvaddr);
//定义信息交流结构体
msg_t msg;
//创建链表头结点
addrlist list = create(1);
//判断链表是否创建
if(list == NULL)
{
printf("创建失败\n");
return -1;
}
struct climsg info;
info.sfd = sfd;
info.list = list;
while(1)
{
//获取用户信息
bzero(&msg, sizeof(msg));
bzero(&rcvaddr, sizeof(rcvaddr));
if(recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&rcvaddr, &addrlen_rcv) < 0)
{
ERR_MSG("recvfrom");
return -1;
}
pthread_t tid;
if(pthread_create(&tid, NULL, deal_cli_msg, &info) != 0)
{
fprintf(stderr, "pthread_create tid1 failed");
return -1;
}
switch (msg.type)
{
case 'L':
//转发登录信息给所有连接用户
sprintf(msg.text, "用户%s登录", msg.name);
printf("[系统]用户%s已登录\n", msg.name);
strcpy(msg.name, "系统");
func_send(list, sfd, msg);
//录入登录用户信息
func_log(list, rcvaddr);
break;
case 'Q':
//删除下线用户信息
func_delete(sfd, msg, list, rcvaddr);
break;
case 'C':
//发送聊天信息
func_send(list, sfd, msg);
break;
default:
printf("rcvfrom error\n");
break;
}
pthread_detach(tid);
}
//关闭套接字
close(sfd);
return 0;
}
int func_delete(int sfd, msg_t msg, addrlist list, struct sockaddr_in rcvaddr)
{
addrlist temp = list;
while(temp->next != NULL)
{
if((rcvaddr.sin_addr.s_addr == temp->next->cin.sin_addr.s_addr) \
&& (rcvaddr.sin_port == temp->next->cin.sin_port))
{
addrlist del = temp->next;
temp->next = del->next;
free(del);
del = NULL;
list->len--;
//转发用户下线信息
sprintf(msg.text, "用户%s下线", msg.name);
printf("[系统]用户%s已下线\n", msg.name);
strcpy(msg.name, "系统");
func_send(list, sfd, msg);
}
if(list->len == 0)
{
printf("所有均用户下线\n");
return 0;
}
if(temp->next != NULL)
temp = temp->next;
}
return 0;
}
void* deal_cli_msg(void* arg)
{
while(1)
{
int sfd = ((struct climsg*)arg)->sfd;
addrlist list= ((struct climsg*)arg)->list;
msg_t msg;
fgets(msg.text, sizeof(msg.text), stdin);
msg.text[strlen(msg.text)-1] = '\0';
strcpy(msg.name, "系统");
func_send(list, sfd, msg);
}
}
int func_log(addrlist list, struct sockaddr_in rcvaddr)
{
//将用户信息插入链表中
//头插 创建一个新节点s
addrlist s = create(0);
if(s == NULL)
{
return -1;
}
//新节点s创建成功 s的数据域赋值
s->cin.sin_port = rcvaddr.sin_port;
s->cin.sin_addr.s_addr = rcvaddr.sin_addr.s_addr;
//s的指针域
s->next = list->next;
list->next = s;
list->len++;
return 0;
}
int func_send(addrlist list, int sfd, msg_t msg)
{
addrlist temp = list;
while(temp->next != NULL)
{
temp = temp->next;
if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(temp->cin), sizeof(temp->cin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
return 0;
}
addrlist create(int flag)
{
//创建头结点list
addrlist list = (struct ADDR*)malloc(sizeof(struct ADDR));
if(list == NULL)
{
return NULL;
}
//对头结点的list的数据域初始化
if(flag == 1)
list->len = 0;
else if(flag == 0)
{
list->cin.sin_family = AF_INET;
list->cin.sin_port = htons(PORT);
list->cin.sin_addr.s_addr = inet_addr(IP);
}
//对头结点的指针域初始化
list->next = NULL;
return list;
}
运行效果