> 文章列表 > 基于UDP的网络聊天室

基于UDP的网络聊天室

基于UDP的网络聊天室

服务器端:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>#define ERR_MSG(msg) do{\\fprintf(stderr,"%d",__LINE__);\\perror("msg");\\
}while(0)#define IP "192.168.0.112"
#define PORT 6767struct msg
{char flag;     //'L'登录,'C'聊天,'Q'下线char name[20];char text[256];
};typedef struct data
{struct sockaddr_in cin;char name[20];struct data* next;
}*Linklist;Linklist LinklistcreateData();
int do_quit(int sfd,Linklist *L,char name[20],struct sockaddr_in cin);
int do_login(int sfd,Linklist* L,struct sockaddr_in cin,char name[20]);
Linklist Linklistcreatehead();
int do_chat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256]);
int do_systemchat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256]);int main(int argc, const char *argv[])
{//创建报式套接字int sfd = socket(AF_INET,SOCK_DGRAM,0);if(sfd < 0){ERR_MSG("socket");return -1;}//sendto有时候会没有权限  error:Permission denied//添加权限int on=1;setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR|SO_BROADCAST,&on,sizeof(on));//创建填充地址信息结构体struct sockaddr_in sin;sin.sin_family   	= AF_INET;sin.sin_port   		= htons(PORT);sin.sin_addr.s_addr = inet_addr(IP);//绑定自身服务器端的IP和端口if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("bind");return -1;}//因为要接收数据,所以要定义一个结构体用来存储客户端的信息struct sockaddr_in cin;socklen_t addrlen = sizeof(cin);char buf[128]="";ssize_t res = 0;//创建进程pid_t cpid = fork();//创建一个链表头Linklist L = Linklistcreatehead();//父进程用于接收if(cpid > 0){struct msg rcvbuf;while(1){//接收信息if(recvfrom(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&cin,&addrlen) <0){ERR_MSG("recvfrom");return -1;}printf("flag = %c,%s\\n",rcvbuf.flag,rcvbuf.name);switch(rcvbuf.flag){case 'L':do_login(sfd,&L,cin,rcvbuf.name);break;case 'C':do_chat(sfd,&L,cin,rcvbuf.name,rcvbuf.text);break;case 'Q':do_quit(sfd,&L,rcvbuf.name,cin);break;case 'S':do_systemchat(sfd,&L,cin,rcvbuf.name,rcvbuf.text);}}}else if(0 == cpid){//子进程给用户发送信息//转发给系统,让系统发送信息给客户端struct msg rcvbuf;rcvbuf.flag = 'S';bzero(rcvbuf.name,sizeof(rcvbuf.name));strcpy(rcvbuf.name,"system");ssize_t res = 0;while(1)	{scanf("%s",rcvbuf.text);while(getchar() != 10);/*if(0 == strcasecmp(rcvbuf.text,"quit")){rcvbuf.flag = 'Q';}*/if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}/*if(0 == strcasecmp(rcvbuf.text,"quit")){close(cfd);break;}*/} }else if(cpid < 0){ERR_MSG("fork");return -1;}//关闭close(sfd);return 0;
}//登录函数
int do_login(int sfd,Linklist* L,struct sockaddr_in cin,char name[20])
{//发送用户登录消息Linklist q = *L;char buf[256] = "";sprintf(buf,"用户%s已登录>>>\\n",name);while(q->next != NULL){q=q->next;if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0){ERR_MSG("sendto");return -1;}}//地址信息写入链表//申请空间Linklist p=LinklistcreateData();//为节点填充数据p->cin=cin;strcpy(p->name,name);q->next = p;return 0; //成功返回0
}//创建链表头
Linklist Linklistcreatehead()
{Linklist L=(Linklist)malloc(sizeof(struct data));if(NULL == L)return NULL;L->cin.sin_family = AF_INET;L->cin.sin_port 	  = htons(-1);L->cin.sin_addr.s_addr = inet_addr("-1.-1.-1.-1");L->next = NULL;return L;
}//创建链表节点
Linklist LinklistcreateData()
{Linklist p =(Linklist)malloc(sizeof(struct data));if(NULL == p)return p;//创建成功p->cin.sin_family = AF_INET;p->cin.sin_port 	  = htons(-1);p->cin.sin_addr.s_addr = inet_addr("-1.-1.-1.-1");p->next = NULL;return p;
}//发送聊天信息
int do_chat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256])
{char buf[256]="";Linklist q=*L;sprintf(buf,"%s:%s\\n",name,text);while(q->next != NULL){q=q->next;if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0){ERR_MSG("sendto");return -1;}} return 0;
}//群发退出消息
int do_quit(int sfd,Linklist *L,char name[20],struct sockaddr_in cin)
{char buf[256]="";Linklist q=*L;sprintf(buf,"用户:%s已退出\\n",name);while(q->next != NULL){if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0){ERR_MSG("sendto");return -1;}	if(strcasecmp(q->name,name) == 0){if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->next->cin),sizeof(cin)) <0){ERR_MSG("sendto");return -1;}	Linklist p=q->next;q->next = p->next;free(p);p=NULL;}else{q=q->next;}}return 0;
}//发送聊天信息
int do_systemchat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256])
{char buf[256]="";Linklist q=*L;sprintf(buf,"%s:%s\\n",name,text);while(q->next != NULL){q=q->next;if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0){ERR_MSG("sendto");return -1;}} return 0;
}

客户端:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>#define ERR_MSG(msg) do{\\fprintf(stderr,"%d",__LINE__);\\perror("msg");\\
}while(0)#define SER_IP "192.168.0.112"
#define SER_PORT 6767#define CLI_IP "192.168.0.111"
#define CLI_PORT 7777struct msg
{char flag;char name[20];char text[256];
};int main(int argc, const char *argv[])
{//创建报式套接字int cfd = socket(AF_INET,SOCK_DGRAM,0);if(cfd < 0){ERR_MSG("socket");return -1;}//创建填充服务器地址信息结构体struct sockaddr_in sin;sin.sin_family   	= AF_INET;sin.sin_port   		= htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);struct sockaddr_in rcvaddr;socklen_t addrlen = sizeof(rcvaddr);//填充协议struct msg rcvbuf;rcvbuf.flag = 'L';printf("请输入你的名字>>>\\n");scanf("%s",rcvbuf.name);while(getchar() != 10);ssize_t res = 0;//发送登录请求if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}printf("发送成功\\n");char buf[256]="";//创建子进程,实现随时收发pid_t cpid=fork();//父进程发送消息if(cpid > 0){rcvbuf.flag = 'C';while(1)	{scanf("%s",rcvbuf.text);if(0 == strcasecmp(rcvbuf.text,"quit")){rcvbuf.flag = 'Q';}if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}if(0 == strcasecmp(rcvbuf.text,"quit")){close(cfd);break;}} }//子进程接收消息else if(0 == cpid){while(1){bzero(buf,sizeof(buf));if(recvfrom(cfd,&buf,sizeof(buf),0,NULL,NULL) < 0){ERR_MSG("fork");return -1;}if(1 == getppid()){printf("父进程退出,子进程准备退出>>>\\n");break;}printf("接收信息成功\\n");printf("%s",buf);}}if(cpid < 0){ERR_MSG("fork");return -1;}//关闭close(cfd);close(cpid);return 0;
}

运行结果:

PS:接收信息成功和父进程退出两句话是测试用的,可以注掉