小项目:基于UDP的网络聊天室
单链表存储数据:linklist.c
#include "linklist.h"
//头节点的创建
linkList *list_creat()
{
linkList *h=(linkList *)malloc(sizeof(linkList));
if(NULL==h){
return NULL;
}
h->next=NULL;
return h;
}
//申请节点
linkList *node_buy(struct sockaddr_in addr)
{
linkList *p=(linkList *)malloc(sizeof(linkList));
if(NULL==p){
return NULL;
}
p->tar_addr=addr;
p->next=NULL;
return p;
}
//判空
int list_empty(linkList *h)
{
if(NULL==h){
return -1;
}
return h->next==NULL ? 1 : 0;
}
//头插法
int list_insert_head(linkList *h,struct sockaddr_in addr)
{
if(NULL==h){
return -1;
}
linkList *l=node_buy(addr);
if(NULL==l){
return -2;
}
l->next=h->next;
h->next=l;
return 0;
}
//删除目标
int list_delete(linkList *h,struct sockaddr_in addr)
{
if(NULL==h){
return -1;
}
if(list_empty(h)){
return -2;
}
linkList *p=h->next;
while(p->next!=NULL){
if(memcmp(&(p->tar_addr),&addr,sizeof(addr))==0)
break;
else
p=p->next;
}
while(h->next!=p){
h=h->next;
}
h->next=p->next;
free(p);
return 0;
}
//寻找目标是否存在
int list_find_exist(linkList *h,struct sockaddr_in addr)
{
if(NULL==h){
return -1;
}
if(list_empty(h)){
return 1;
}
while(h->next!=NULL){
h=h->next;
if(memcmp(&(h->tar_addr),&addr,sizeof(addr))==0)
return -1;
}
return 1;
}
//头删
int list_delete_head(linkList *h)
{
if(NULL==h){
return -1;
}
if(!list_empty(h)){
linkList *p=h->next;
h->next=p->next;
free(p);
}else{
return -1;
}
return 0;
}
//销毁链表
int list_destroy(linkList *h)
{
while(list_delete_head(h)==0);
return 0;
}
单链表头文件:linklist.h
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
typedef struct Node{
struct sockaddr_in tar_addr;
struct Node *next;
}linkList;
linkList *list_creat();
linkList *node_buy(struct sockaddr_in addr);
int list_empty(linkList *h);
int list_insert_head(linkList *h,struct sockaddr_in addr);
int list_delete(linkList *h,struct sockaddr_in addr);
int list_find_exist(linkList *h,struct sockaddr_in addr);
int list_delete_head(linkList *h);
int list_destroy(linkList *h);
#endif
客户端:chatClient.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "./linklist.h"
#include <semaphore.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%s__ __%d__\n",__func__,__LINE__);\
perror(msg);\
}while(0)
#define N 32
#define MAX 128
typedef struct {
char flag;
char username[N];
char buf[MAX];
}Msgbag;
int cfd;
int label=0;
//给服务器发送消息
void *callBackSnd(void *arg)
{
Msgbag bag;
struct sockaddr_in tar=*(struct sockaddr_in *)arg;
memset(&bag,0,sizeof(bag));
bag.flag='u';
printf("请输入你的用户名>>>");
fgets(bag.username,sizeof(bag.username),stdin);
bag.username[strlen(bag.username)-1]=0;
if(sendto(cfd,&bag,sizeof(bag),0,(struct sockaddr *)&tar,sizeof(tar))<0){
ERR_MSG("sendto");
exit(-1);
}
while(1){
bzero(bag.buf,sizeof(bag.buf));
fgets(bag.buf,sizeof(bag.buf),stdin);
bag.buf[strlen(bag.buf)-1]=0;
if(label){
continue;
}
if(sendto(cfd,&bag,sizeof(bag),0,(struct sockaddr *)&tar,sizeof(tar))<0){
ERR_MSG("sendto");
exit(-1);
}
if(strcasecmp(bag.buf,"quit")==0){
exit(0);
}
}
pthread_exit(NULL);
}
//客户端接受来自服务器的消息
void *callBackRcv(void *arg)
{
Msgbag bag;
struct sockaddr_in tar=*(struct sockaddr_in *)arg;
while(1){
memset(&bag,0,sizeof(bag));
if(recv(cfd,&bag,sizeof(bag),0)<0){
ERR_MSG("recv");
exit(-1);
}
if(bag.flag=='s'){
printf("%s\n",bag.buf);
}else if(bag.flag=='t'){
if(strcasecmp(bag.username,"system")==0){
printf("系统提醒:%s\n",bag.buf);
}else if(strcasecmp(bag.username,"stop")==0){
label=1;
printf("系统提示:全体禁言\n");
}else if(strcasecmp(bag.username,"continue")==0){
label=0;
printf("系统提示:解除全体禁言\n");
}else{
printf("系统提示:群聊解散\n");
exit(0);
}
}else{
printf("%s:%s\n",bag.username,bag.buf);
}
}
}
int main(int argc, const char *argv[])
{
if(argc<3){
fprintf(stderr,"[传参不足]:%s IP PORT\n",argv[0]);
return -1;
}
//创建UDP套接字
cfd=socket(AF_INET,SOCK_DGRAM,0);
if(cfd<0){
ERR_MSG("socket");
return -1;
}
//填充目标服务器的地址信息结构体
struct sockaddr_in tar;
tar.sin_family=AF_INET;
tar.sin_port=htons(atoi(argv[2]));
tar.sin_addr.s_addr=inet_addr(argv[1]);
//创建两个线程执行不同任务
pthread_t rid,sid;
if(pthread_create(&rid,NULL,callBackRcv,(void *)&tar)!=0){
ERR_MSG("pthread_create");
return -1;
}
if(pthread_create(&sid,NULL,callBackSnd,(void *)&tar)!=0){
ERR_MSG("pthread_create");
return -1;
}
pthread_join(rid,NULL);
pthread_join(sid,NULL);
close(cfd);
return 0;
}
服务器:chatServe.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "./linklist.h"
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%s__ __%d__\n",__func__,__LINE__);\
perror(msg);\
}while(0)
#define N 32
#define MAX 128
typedef struct {
char flag;
char username[N];
char buf[MAX];
}Msgbag;
int sfd;
int sendtoall(linkList *p,struct sockaddr_in *temp,Msgbag bag)
{
int flag=0;
if(NULL==temp)
flag=1;
while(!list_empty(p)){
p=p->next;
if(!flag){
if(memcmp(&(p->tar_addr),temp,sizeof(struct sockaddr_in))==0)
continue;
}
if(sendto(sfd,&bag,sizeof(bag),0,(struct sockaddr *)&(p->tar_addr),\
sizeof(p->tar_addr))<0){
ERR_MSG("sendto");
exit(-1);
}
}
return 0;
}
void *callBackRcv(void *arg)
{
linkList *userdata=(linkList *)arg;
struct sockaddr_in temp;
socklen_t addrlen=sizeof(temp);
Msgbag bag;
while(1){
memset(&bag,0,sizeof(bag));
if(recvfrom(sfd,&bag,sizeof(bag),0,(struct sockaddr*)&temp,&addrlen)<0){
ERR_MSG("recv");
exit(-1);
}
if(list_find_exist(userdata,temp)==1){
list_insert_head(userdata,temp);
printf("%s [ %s : %d ] join\n",\
bag.username,inet_ntoa(temp.sin_addr),ntohs(temp.sin_port));
bag.flag='s';
sprintf(bag.buf,"*****%s加入群聊*****",bag.username);
sendtoall(userdata,&temp,bag);
}else{
//将消息转发给除自己外的所有用户
if(strcasecmp(bag.buf,"quit")==0){
list_delete(userdata,temp);
printf("%s [ %s : %d ] quit\n",\
bag.username,inet_ntoa(temp.sin_addr),ntohs(temp.sin_port));
bag.flag='s';
sprintf(bag.buf,"*****%s退出群聊*****",bag.username);
sendtoall(userdata,NULL,bag);
}else{
bag.flag='u';
printf("%s [ %s : %d ] chat\n",\
bag.username,inet_ntoa(temp.sin_addr),ntohs(temp.sin_port));
sendtoall(userdata,&temp,bag);
}
}
}
pthread_exit(NULL);
}
//发送系统消息
void *callBackSnd(void *arg)
{
linkList *userdata=(linkList *)arg;
Msgbag bag;
bag.flag='t';
while(1){
strcpy(bag.username,"system");
bzero(bag.buf,sizeof(bag.buf));
fgets(bag.buf,sizeof(bag.buf),stdin);
bag.buf[strlen(bag.buf)-1]=0;
if(strcasecmp(bag.buf,"stop")==0){
strcpy(bag.username,bag.buf);
}else if(strcasecmp(bag.buf,"continue")==0){
strcpy(bag.username,bag.buf);
}else if(strcasecmp(bag.buf,"quit")==0){
strcpy(bag.username,bag.buf);
}
sendtoall(userdata,NULL,bag);
if(strcasecmp(bag.buf,"quit")==0){
list_destroy(userdata);
}
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
if(argc<3){
fprintf(stderr,"[传参不足]:%s IP PORT\n",argv[0]);
return -1;
}
//创建UDP套接字
sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0){
ERR_MSG("socket");
return -1;
}
//填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(atoi(argv[2]));
sin.sin_addr.s_addr=inet_addr(argv[1]);
//绑定ip和端口号
if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))<0){
ERR_MSG("bind");
return -1;
}
//创建一个链表保存用户及其主机信息
linkList *userdata=list_creat();
//创建两个线程执行不同任务
pthread_t rid,sid;
if(pthread_create(&rid,NULL,callBackRcv,(void *)userdata)!=0){
ERR_MSG("pthread_create");
return -1;
}
if(pthread_create(&sid,NULL,callBackSnd,(void *)userdata)!=0){
ERR_MSG("pthread_create");
return -1;
}
pthread_join(rid,NULL);
pthread_join(sid,NULL);
return 0;
}
Makefile:
TARGET?=main
SER:=ser
CLI:=cli
CAN:=-c -o
CC:=gcc
PD:=-pthread
OBJS:=chatServe.o linklist.o
OBJC:=chatClient.o linklist.o
$(TARGET):$(OBJS) $(OBJC)
$(CC) $(OBJS) -o $(SER) $(PD)
$(CC) $(OBJC) -o $(CLI) $(PD)
%.o:%.c
$(CC) $< $(CAN) $@
clean:
rm -rf $(OBJS) $(OBJC) $(SER) $(CLI)
测试: