项目:基于UDP的网络聊天室
项目需求:
1.
如果有用户登录,其他用户可以收到这个人的登录信息
2.
如果有人发送信息,其他用户可以收到这个人的群聊信息
3.
如果有人下线,其他用户可以收到这个人的下线信息
4.
服务器可以发送系统信息
服务器代码:
#include"aa.h"
#include<unistd.h>
#define IP "192.168.31.135"
pthread_t p1;
pthread_t p2;
int fp;
void*func1(void*arg)
{
//链表
list* h = (list*)arg;
//客户端
struct sockaddr_in cin;
socklen_t len = sizeof(cin);
//接收客户端数据
char buf[128] = "";
//接收IP
char ip[128]="";
//接收端口号
int num;
while(1)
{
bzero(buf,sizeof(buf));
//接收客户端消息
if(recvfrom(fp,buf,sizeof(buf),0,(struct sockaddr*)&cin,&len)<0)
{
MSG("recvfrom");
return NULL;
}
bzero(ip,sizeof(ip));
//存储ip和端口号
strcpy(ip,inet_ntoa(cin.sin_addr));
num = ntohs(cin.sin_port);
//printf("111\n");
//printf("%s %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
//判断该客户端是否已存在
int flag = 0; // flag=1存在
list*p;
p = h;
while(p->next!=NULL)
{
p = p->next;
if(ntohs(p->cin.sin_port) == num)
{
flag = 1;
break;
}
}
if(flag == 0 )
{
char name[50]= "";
strcpy(name,buf);
name[strlen(name)-18] = '\0';
list_insert(h,num,ip,name);
list *p1 = h;
while(p1->next != NULL)
{
//将新加入用户信息传递给其他用户
p1 = p1->next;
//当前接收用户,将消息发送给其他用户
if(ntohs(p1->cin.sin_port)!=num)
{
if(sendto(fp,buf,sizeof(buf),0,(struct sockaddr*)&p1->cin,sizeof(p1->cin))<0)
{
MSG("sendto");
return NULL;
}
}
}
}
//向其他用户传输信息
//找到当前客户端用户位置
list*pp = h;
while(pp->next != NULL)
{
pp = pp->next;
if(ntohs(pp->cin.sin_port)!=num)
break;
}
if(flag==1)
{
//找到当前客户端用户位置
list*pp = h;
while(pp->next != NULL)
{
pp = pp->next;
if(ntohs(pp->cin.sin_port)==num)
break;
}
//如果客户端退出,则通知所有用户
if(strcmp(buf,"quit")==0)
{
char bb[50]="";
bzero(bb,sizeof(bb));
strcpy(bb,pp->name);
strcat(bb,"已退出群聊");
list_delet(h,num); //删除用户
list*qq = h;
while(qq->next!=NULL)
{
qq = qq->next;
if(sendto(fp,bb,sizeof(bb),0,(struct sockaddr*)&qq->cin,sizeof(qq->cin))<0)
{
MSG("sendto");
return NULL;
}
}
continue;
}
char buf1[128] = "";
bzero(buf1,sizeof(buf1));
strcpy(buf1,pp->name);
strcat(buf1,":");
strcat(buf1,buf);
list*a1 = h;
while(a1->next != NULL)
{
a1 = a1->next;
//如果不是当前接收用户,则将消息发送给其他用户
if(ntohs(a1->cin.sin_port)!=num)
{
if(sendto(fp,buf1,sizeof(buf1),0,(struct sockaddr*)&a1->cin,sizeof(a1->cin))<0)
{
MSG("sendto");
return NULL;
}
}
}
}
}
}
void*func2(void*arg)
{
while(1)
{
list*h = (list*)arg;
list*pp = h;
char buf[50] = "";
char buf1[128] = "系统消息";
//系统消息
scanf("%s",buf);
while(getchar()!='\n');
strcat(buf1,buf);
while(pp->next!=NULL)
{
pp = pp->next;
//发送客户端消息
if(sendto(fp,buf1,sizeof(buf1),0,(struct sockaddr*)&pp->cin,sizeof(pp->cin))<0)
{
MSG("sendto");
return NULL;
}
}
}
}
int main(int argc, const char *argv[])
{
fp = socket(AF_INET,SOCK_DGRAM,0);
//创建服务器套接字
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr(IP);
if(bind(fp,(struct sockaddr*)&sin,sizeof(sin))<0)
{
MSG("bind");
return -1;
}
//创建链表
list*h = list_create();
//创建线程
pthread_create(&p1,NULL,func1,(void*)h);
pthread_create(&p2,NULL,func2,(void*)h);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
return 0;
}
功能函数:
#include"aa.h"
list*list_create()
{
list*h = (list*)malloc(sizeof(list));
h -> next = NULL;
if(h==NULL)
{
printf("链表创建失败\n");
return NULL;
}
return h;
}
void list_insert(list*h,int port,char*addr,char*name)
{
//插入结点赋值
list*temp = (list*)malloc(sizeof(list));
strcpy(temp->name , name);
temp->cin.sin_family = AF_INET;
temp->cin.sin_port = htons(port);
temp->cin.sin_addr.s_addr = inet_addr(addr);
temp->next = h->next;
h->next = temp;
}
void list_delet(list*h,int port)
{
list*temp = h;
while(temp->next!=NULL)
{
if( temp->next->cin.sin_port == htons(port))
break;
temp = temp->next;
}
list*p = temp->next;
temp->next = p->next;
free(p);
}
库函数:
#ifndef _pp_
#define _pp_
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<pthread.h>
#define MSG(msg) do{\
printf("__%d__",__LINE__);\
perror(msg);\
}while(0)
typedef struct note
{
char name[20]; //客户端姓名
struct sockaddr_in cin;
//int port ; //客户端端口号
//char addr[128]; //客户端ip地址
struct note* next;
}list;
//创建链表
list*list_create();
//memcmp结构体比较函数
//插入结点
void list_insert(list*h,int port,char*addr,char*name);
//删除结点
void list_delet(list*h,int port);
#endif
客户端代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include<pthread.h>
//打印错误新的宏函数
#define ERR_MSG(msg) do{\
fprintf(stderr, " __%d__ ", __LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.31.135"
typedef struct
{
char addr[20];
int port;
}msg;
pthread_t p1;
pthread_t p2;
int sfd;
msg dd;
void*func1(void*arg)
{
printf("请输入用户ID:");
char name[128];
scanf("%s",name);
while(getchar()!='\n');
strcat(name,"已进入聊天群");
sendto(sfd,name,sizeof(name),0,(struct sockaddr*)arg,sizeof(*(struct sockaddr_in*)arg));
char buf[128] = "";
while(1)
{
bzero(buf, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = 0;
//将数据包发送给服务器,所以地址信息结构体需要填服务器的信息
if(sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)arg, sizeof(*(struct sockaddr_in*)arg)) < 0)
{
ERR_MSG("sendto");
return NULL;
}
if(strcmp(buf,"quit")==0)
{
exit(0);
}
}
}
void*func2(void*arg)
{
char buf[128] = "";
while(1)
{
//接收
bzero(buf, sizeof(buf));
//接收服务器发送过来的数据包
if(recvfrom(sfd, buf, sizeof(buf), 0, NULL,NULL )< 0)
{
ERR_MSG("recvfrom");
return NULL;
}
printf("%s\n",buf);
}
}
int main(int argc, const char *argv[])
{
//创建服务器套接字
sfd = socket(AF_INET,SOCK_DGRAM,0);
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//填充服务器的IP地址以及端口号 -->因为客户端要主动发送数据包给服务器
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr(IP);
//创建线程
pthread_create(&p1,NULL,func1,(void*)&sin);
pthread_create(&p2,NULL,func2,(void*)&sin);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
return 0;
}
结果演示: