文章目录
服务端
功能
1.将客户端登录信息发送给其他已登录的用户
2.将客户端发送的消息发送给其他已登录的用户
3.将客户端退出信息发送给其他已登录的用户
4.将客户端的信息显示在本机上
代码
ser.h
#ifndef __UDPSER_H__
#define __UDPSER_H__
//链表结构体
typedef struct node
{
union
{
int num; //群人数
struct sockaddr_in cin; //群员地址信息结构体
};
struct node *next;
}linkList;
//错误返回
#define ERR_MSG(msg) do{\
fprintf(stderr,"line: %d ",__LINE__);\
perror(msg);}while(0);
//创建链表
linkList * link_create();
//头插
int link_insert_head(linkList *L,struct sockaddr_in s);
//遍历发送
void list_show(int sfd,linkList *L,char* buf,int size ,struct sockaddr_in *rcv);
//删除退出成员的地址信息结构体
int link_del(linkList *L,struct sockaddr_in s);
//子线程函数
void *handler1(void* arg);
//获得名字
void getname(char* name,char* buf1);
//收到登入后的回应函数
void load_res(int sfd,linkList *L,struct sockaddr_in rcv,char* name,char *buf);
//收到退出后的回应函数
void quit_res(int sfd,linkList *L,struct sockaddr_in rcv,char* name,char *buf);
#endif
ser.c
子线程函数
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<pthread.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include"ser.h"
//子线程函数
void *handler1(void* arg)
{
extern int sfd;
linkList *L=(linkList*)arg;
char buf2[128]="";
int size=sprintf(buf2,"\n%s","--------------服务器:");
while(1)
{
bzero(buf2+size,sizeof(buf2)-size);
fgets(buf2+size,sizeof(buf2)-size,stdin);
//遍历发送
list_show(sfd,L,buf2,strlen(buf2),NULL);
}
}
创建链表函数
//创建链表
linkList * link_create()
{
linkList *L=(linkList *)malloc(sizeof(linkList));
if(NULL==L)
{
printf("创建列表失败\n");
exit(-1); //直接退出程序
}
//初始化
L->num=0;
L->next=NULL;
printf("链表创建成功\n");
return L;
}
头插函数
//头插
int link_insert_head(linkList *L,struct sockaddr_in s)
{
//判断条件
if(L==NULL)
{
printf("链表不合法\n");
return -1;
}
//判断客户端是否重复输入
//链表查重
linkList *q=L->next;
while(q!=NULL)
{
if( (q->cin.sin_port==s.sin_port) && (q->cin.sin_addr.s_addr==s.sin_addr.s_addr))
{
return -2; //代表客户端重复登入
}
q=q->next;
}
//申请结点
linkList *p=(linkList *)malloc(sizeof(linkList));
if(p==NULL)
{
printf("节点申请失败\n");
return -2;
}
//写入数据
p->next=NULL;
p->cin=s;
//头插
p->next=L->next;
L->next=p;
//表的变化
L->num++;
// printf("存入数据成功\n");
return 0;
}
删除退出成员的地址信息结构体函数
//删除退出成员的地址信息结构体
int link_del(linkList *L,struct sockaddr_in s)
{
//判断条件
if(L==NULL)
{
printf("链表不合法\n");
return -1;
}
//查找删除对象
linkList *p=L; //目标前驱
linkList *q=p->next; //目标
while(q!=NULL)
{
if( (q->cin.sin_port==s.sin_port) && (q->cin.sin_addr.s_addr==s.sin_addr.s_addr))
{
//删除客户端地址信息结构体
p->next=q->next;
//释放空间
free(q);
q=NULL;
//表的变化
L->num--;
printf("删除数据成功\n");
return 0;
}
p=q;
q=q->next;
}
if(q==NULL)
{
return -2; //表未找到目标
}
}
遍历发送函数
//遍历发送
void list_show(int sfd,linkList *L,char* buf,int size,struct sockaddr_in *rcv)
{
//判断逻辑
if(NULL==L || L->next==NULL)
{
printf("表空,无发送对象\n");
return;
}
//遍历
linkList *q=L->next;
while(q!=NULL)
{
if(rcv==NULL || (q->cin.sin_port!=rcv->sin_port) || (q->cin.sin_addr.s_addr!=rcv->sin_addr.s_addr))
{
if(sendto(sfd,buf,size,0,(struct sockaddr*)&(q->cin),sizeof(q->cin))==-1)
{
ERR_MSG("sendto");
exit(-1);
}
}
q=q->next;
}
}
获得名字函数
//获得名字
name实际最大字节数为的19;
void getname(char* name,char* buf1)
{
bzero(name,sizeof(name));
int i=1;
while(buf1[i]!=0 && i!=20)
{
name[i-1]=buf1[i];
i++;
}
name[i]=0;
}
收到登入后的回应函数
//收到登入后的回应函数
void load_res(int sfd,linkList *L,struct sockaddr_in rcv,char* name,char *buf)
{
//插入客户端地址信息结构体
int x=link_insert_head(L,rcv);
if(x!=0)
{
if(x==-2)
{
printf("[%s:%d]%s:重复登入\n",inet_ntoa(rcv.sin_addr),ntohs(rcv.sin_port),name);
return;
}
if(x==-1)
exit(-1);
}
//在服务器上显示消息
printf("[%s:%d]%s:登入成功\n",inet_ntoa(rcv.sin_addr),ntohs(rcv.sin_port),name);
printf("现在有%d个人在群内\n",L->num);
//发送客户端登入信息
int size=sprintf(buf,"\n------------------%s%c%s",name,':',"上线\n");
list_show(sfd,L,buf,size,&rcv);
}
收到退出后的回应函数
//收到退出后的回应函数
void quit_res(int sfd,linkList *L,struct sockaddr_in rcv,char* name,char *buf)
{
//删除退出成员的地址信息结构体
int x=link_del(L,rcv);
if(x!=0)
{
if(x==-2)
{
printf("重复退出,链表内无该对象[%s:%d]%s\n",inet_ntoa(rcv.sin_addr),ntohs(rcv.sin_port),name);
return;
}
}
//在服务器上显示消息
printf("[%s:%d]%s:退出群聊\n",inet_ntoa(rcv.sin_addr),ntohs(rcv.sin_port),name);
printf("现在有%d个人在群内\n",L->num);
//发送客户端登入信息
int size=sprintf(buf,"\n-----------------%s%c%s",name,':',"下线\n");
list_show(sfd,L,buf,size,&rcv);
}
main.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<pthread.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include"ser.h"
//全局变量
int sfd;
int main(int argc, const char *argv[])
{
//创建报式套接字
sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
ERR_MSG("socket");
return -1;
}
printf("socket success\n");
//允许端口快速重用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
//服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family =AF_INET;
sin.sin_port =htons(8888);
sin.sin_addr.s_addr =inet_addr("192.168.31.10");
//客户端的地址信息结构体
struct sockaddr_in rcv;
socklen_t addrlen=sizeof(rcv);
//绑定套接字
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
//创建列表
linkList *L=link_create();
//创建分支线程
pthread_t tid;
pthread_create(&tid,NULL,handler1,(void*)L);
//主线程
char buf1[128]=""; //客户端信息
char buf[128]=""; //发给其他客户端的消息
char name[20]=""; //客户端名字
while(1)
{
bzero(buf1,sizeof(buf1));
bzero(buf,sizeof(buf));
//接收信息
if(recvfrom(sfd,buf1,sizeof(buf1),0,(struct sockaddr*)&rcv,&addrlen)<0)
{
ERR_MSG("recvfrom");
return -1;
}
//获得名字
getname(name,buf1);
//发送客户端信息(登入,消息,退出)(遍历发送)
//登入 将客户端的地址信息结构体储存进链表
if(buf1[0]=='L')
{
load_res(sfd,L,rcv,name,buf);
}
//群聊
if(buf1[0]=='C')
{
//在服务器上显示消息
printf("[%s:%d]%s:发送信息:%s\n",inet_ntoa(rcv.sin_addr),ntohs(rcv.sin_port),name,buf1+strlen(name)+2);
//发送客户端登入信息
int size=sprintf(buf,"\n%s%c%s%s",name,':',buf1+strlen(name)+2,"\n");
list_show(sfd,L,buf,size,&rcv);
}
//退出(quit) 将客户端的地址信息结构体移出链表
if(buf1[0]=='Q')
{
quit_res(sfd,L,rcv,name,buf);
}
}
//退出服务器
return 0;
}
客户端
功能
1.发送登录信息发送给服务端
2.将发送的消息发送给服务端
3.将退出信息发送给服务端
4.接收服务端发送的消息
代码
cli.h
#ifndef __UDPSER_H__
#define __UDPSER_H__
//错误返回
#define ERR_MSG(msg) do{\
fprintf(stderr,"line: %d ",__LINE__);\
perror(msg);}while(0);
//子线程函数
void *handler1(void* arg);
//发消息函数
void my_send(int sfd,char signal,char *buf,int size,const struct sockaddr_in sin);
#endif
cli.c
子线程函数
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<pthread.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include"cli.h"
//子线程函数
void *handler1(void* arg)
{
extern int sfd;
char buf[128];
//接收的地址信息结构体
struct sockaddr_in rcv;
socklen_t addrlen=sizeof(rcv);
while(1)
{
bzero(buf,sizeof(buf));
//接收信息
if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&rcv,&addrlen)<0)
{
ERR_MSG("recvfrom");
return NULL;
}
printf("%s",buf);
}
}
发消息函数
//发消息函数
void my_send(int sfd,char signal,char *buf,int size,const struct sockaddr_in sin)
{
buf[0]=signal;
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return;
}
}
main.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<pthread.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include"cli.h"
//全局变量
int sfd;
int main(int argc, const char *argv[])
{
//创建报式套接字
sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
ERR_MSG("socket");
return -1;
}
printf("socket success\n");
//允许端口快速重用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
//服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family =AF_INET;
sin.sin_port =htons(8888);
sin.sin_addr.s_addr =inet_addr("192.168.31.10");
//创建分支线程 用于接收服务器发来的信息
pthread_t tid;
pthread_create(&tid,NULL,handler1,NULL);
char buf[128]=""; //客户端信息
char name[20]; //客户名字
printf("请输入用户名:");
fgets(name,sizeof(name),stdin);
name[strlen(name)-1]=0;
int size=sprintf(buf+1,"%s%c",name,0)+1;
//主线程
int flag=0;
while(1)
{
bzero(buf+size,sizeof(buf)-size);
//发送客户端信息(登入,消息,退出)
//登入
if(flag==0)
{
my_send(sfd,'L',buf,size,sin);
printf("上线成功\n");
flag=1;
}
printf("请输入-->"); //客户端发消息或退出
fflush(stdout);
fgets(buf+size,sizeof(buf)-size,stdin);
int size1=strlen(buf+size)+size; //buf实际长度
buf[size1-1]=0;
if(strcasecmp(buf+size,"quit")==0) //退出客户端,下线
{
my_send(sfd,'Q',buf,size,sin);
printf("下线成功\n");
return 0;
}
else //群聊
{
my_send(sfd,'C',buf,size1,sin);
printf("发送成功\n");
}
}
return 0;
}