客户端代码:
#include <myhead.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__:",__LINE__);\
perror(msg);\
}while(0)
#define PORT 8888
#define IP "192.168.1.4"
typedef struct{
char type;
char name[20];
char text[128];
}MSG;
struct ARG{
MSG msg;
int cfd;
struct sockaddr_in sin;
};
void *task(void *arg);
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1 == cfd)
{
ERR_MSG("socket");
return -1;
}
//填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT); //端口号
sin.sin_addr.s_addr = inet_addr(IP); //本机IP
//发送登录信息给服务器
MSG msg;
memset(&msg, 0, sizeof(msg));
printf("请输入用户名:");
fgets(msg.name, sizeof(msg.name), stdin);
msg.name[strlen(msg.name)-1]='\0';
msg.type = 'L';
if(sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){
ERR_MSG("sendto");
return -1;
}
pthread_t tid;
struct ARG arg;
arg.msg = msg;
arg.cfd = cfd;
arg.sin = sin;
//创建线程
if(pthread_create(&tid, NULL, task, &arg)){
ERR_MSG("pthread create");
return -1;
}
while(1){
if(recvfrom(cfd, &msg, sizeof(msg), 0, NULL, NULL) < 0){
perror("recvfrom");
return -1;
}
printf("%s\n",msg.text);
}
pthread_detach(tid);
//关闭文件描述符
close(cfd);
return 0;
}
void *task(void *arg){
MSG msg = ((struct ARG*)arg)-> msg;
int cfd = ((struct ARG*)arg)-> cfd;
struct sockaddr_in sin = ((struct ARG*)arg)-> sin;
while(1){
bzero(msg.text, sizeof(msg.text));
//发送数据包
fgets(msg.text, sizeof(msg.text), stdin);
msg.text[strlen(msg.text)-1] = '\0';
if(!strcmp(msg.text,"quit"))
{
msg.type = 'Q';
}
else
{
msg.type = 'C';
}
if(sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin))<0)
{
ERR_MSG("sendto");
return NULL;
}
if(msg.type == 'Q')
{
return NULL;
}
}
}
服务器端代码:
#include <myhead.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__:", __LINE__);\
perror(msg);\
}while(0)
#define PORT 8888
#define IP "192.168.1.4"
//收发信息的结构体
typedef struct{
char type;
char name[20];
char text[128] ;
}MSG;
//定义保存客户端网络信息结构体的链表类型
typedef struct node{
struct sockaddr_in cin;
struct node* next;
}Node,*LinklistPtr;
int do_recv(int sfd, LinklistPtr head);
int do_login(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin);
int do_quit(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin);
int do_chat(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin);
int do_system(int sfd, struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0){
perror("socket");
return -1;
}
//绑定服务器ip和端口号
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){
perror("bind");
return -1;
}
//创建进程
int pid = 0;
pid = fork();
if(pid > 0){
//父进程, 接收消息
LinklistPtr head = (LinklistPtr)malloc(sizeof(Node));
head->next = NULL;
do_recv(sfd , head);
}
else if(pid==0){
//子进程,发送系统消息
do_system(sfd, sin);
}
//关闭套接字
close(sfd);
return 0;
}
int do_system(int sfd, struct sockaddr_in sin){
MSG msg2 = {'C', "--system--"};
while(1){
bzero(msg2.text, sizeof(msg2.text));
fgets(msg2.text, sizeof(msg2.text), stdin);
msg2.text[strlen(msg2.text)-1] = '\0';
//将当前进程当做客户端,父进程当做服务器,发送信息;
if(sendto(sfd, &msg2, sizeof(msg2), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){
ERR_MSG("sendto");
return -1;
}
}
return 0;
}
int do_recv(int sfd, LinklistPtr head){
MSG msg1;
int recv_len = 0;
struct sockaddr_in cin;
socklen_t clen = sizeof(cin);
while(1){
//接收消息
recv_len = recvfrom(sfd, &msg1, sizeof(msg1), 0, (struct sockaddr*)&cin, &clen);
if(recv_len < 0){
ERR_MSG("recvfrom");
return -1;
}
switch(msg1.type){
case 'L':
//登录函数
do_login(sfd, head, msg1, cin);
break;
case 'C':
//群聊函数
do_chat(sfd, head, msg1, cin);
break;
case 'Q':
//退出函数
do_quit(sfd, head, msg1, cin);
break;
}
}
}
int do_chat(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin){
printf("%s [%s:%d]chat成功\n", msg1.name,\
(char*)inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
char buf[200] = "";
sprintf(buf, "%s:%s", msg1.name, msg1.text);
strcpy(msg1.text, buf);
//循环发送,除了自己以外的ip地址
while(head->next != NULL){
head = head->next;
if(memcmp(&cin, &head->cin, sizeof(cin)) != 0){
if(sendto(sfd, &msg1, sizeof(msg1),0, (struct sockaddr*)&(head->cin), sizeof(head->cin))<0){
ERR_MSG("sendto");
return -1;
}
}
}
return 0;
}
int do_quit(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin){
printf("%s [%s:%d]下线了\n", msg1.name,\
(char*)inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
sprintf(msg1.text, "-----%s已下线-----", msg1.name);
//遍历链表 是自己就将自己在链表中删除
//不是自己 就发送 退出群聊的数据
while(head->next != NULL){
if(memcmp(&cin, &head->next->cin, sizeof(cin)) == 0){
//从链表中删除该客户端信息
LinklistPtr p = head->next;
head->next = p->next;
free(p);
p=NULL;
}
else{
head = head->next;
if(sendto(sfd, &msg1, sizeof(msg1),0, (struct sockaddr*)&(head->cin), sizeof(head->cin))<0){
ERR_MSG("sendto");
return -1;
}
}
}
return 0;
}
int do_login(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin){
printf("%s [%s:%d]登录成功\n", msg1.name,\
(char*)inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
sprintf(msg1.text, "-----%s登录成功-----", msg1.name);
//先遍历链表 将新用户加入群聊的消息发给所有人
while(head->next != NULL){
head = head->next;
if(sendto(sfd, &msg1, sizeof(msg1), 0, (struct sockaddr*)&(head->cin), sizeof(head->cin))<0){
ERR_MSG("sendto");
return -1;
}
}
//将登录成功的客户端封装成结点
LinklistPtr p = (LinklistPtr)malloc(sizeof(Node));
p->cin = cin;
p->next= NULL;
//将登录成功的客户端信息添加到链表中
head->next = p;
return 0;
}