网络聊天室UDP实现

服务器端:

头文件:

#include

//定义客户信息结构体

typedef struct magtye

{

char type; //消息类型

char name[100]; //客户姓名

char text[1024]; //客户发送聊天信息

}msg_t;

//定义结构体存储每个客户端的ip地址和端口号

typedef struct IP_PORT

{

struct sockaddr_in cin;//地址信息

struct IP_PORT *next;//

}*addrlist;

void usr_login(int sfd,msg_t msg,addrlist *head,struct sockaddr_in cin);

void usr_chat(int sfd,msg_t msg,addrlist head,struct sockaddr_in cin);

void usr_quit(int sfd,msg_t msg,addrlist *head,struct sockaddr_in cin);

主函数:

#include"head.h"

int main(int argc, const char *argv[])

{

//创建套接字

int sfd=-1;

sfd=socket(AF_INET,SOCK_DGRAM, 0);

if(sfd==-1)

{

perror("socket error");

return -1;

}

//将端口号快速重用

int reuse=1;

if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)

{

perror("setsockopt error");

return -1;

}

//服务器进行绑定

//(1)、从终端获取端口号和地址

char SER_IP[100];

int SER_PORT;

printf("请输入服务器ip地址和端口号:");

scanf("%s %d",SER_IP,&SER_PORT);

getchar();//吸收垃圾字符

//(2)、填充服务器地址信息结构体

struct sockaddr_in sin;

sin.sin_family=AF_INET; //地址族

sin.sin_port=htons(SER_PORT); //端口号

sin.sin_addr.s_addr=inet_addr(SER_IP); //IP地址

socklen_t sin_len=sizeof(sin);

//(3)、绑定

if(bind(sfd,(struct sockaddr*)&sin,sin_len)==-1)

{

perror("bind error");

return -1;

}

//定义客户端网络信息结构体

struct sockaddr_in cin;

socklen_t cin_len=sizeof(cin);

msg_t msg;//定义客户发送消息的机构体变量

//创建父子进程处理不同的操作

int pid=fork();

if(pid==-1)

{

perror("fork error");

return -1;

}

if(pid==0)//字进程实现服务器接收消息类型

{

addrlist head=NULL;//链表头指针;

while(1)

{

memset(&msg,0,sizeof(msg));

memset(&cin,0,sizeof(cin));

//接收客户端发来的信息,并判断属于哪种消息类型

recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&cin_len);

switch(msg.type)

{

case 'L': //该消息类型代表新用户上线

{

//创建新节点保存新用户的ip地址和端口号

//向其他在线的用户发送新用户上线通知

usr_login(sfd,msg,&head,cin);

}break;

case 'C': //该消息类型代表一个用户发送消息给其他用户

{

usr_chat(sfd,msg,head,cin);

}break;

case 'Q': //该消息类型代表一个用户下线

{

usr_quit(sfd,msg,&head,cin);

}break;

}

}

}

else if(pid>0)//父进程实现服务器对客户端发送消息

{

strcpy(msg.name,"服务器消息");

msg.type='C';

while(1)

{

memset(msg.text,0,sizeof(msg.text));

fgets(msg.text,sizeof(msg.text),stdin);

msg.text[strlen(msg.text)-1]='\0';

sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len);

if(strcmp(msg.text,"服务器下线")==0)

{

sleep(1);

break;

}

}

kill(pid,SIGKILL);//服务器下线杀死子进程

}

wait(NULL);

//关闭套接字

close(sfd);

return 0;

}

自定义函数:

#include"head.h"

//用户登录操作函数

void usr_login(int sfd,msg_t msg,addrlist *head,struct sockaddr_in cin)

{

//1、创建新节点

addrlist s=(addrlist)malloc(sizeof(addrlist));

s->next=NULL;

//存储新用户的ip地址和端口号

printf("%s : %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));

s->cin=cin;

s->next=*head;

*head=s;

//2、遍历链表将新用户上线消息发送给其他在线用户

addrlist p=*head;

while (p!=NULL)

{

if(p->cin.sin_port!=cin.sin_port)

{

sendto(sfd, &msg, sizeof(msg),0,(struct sockaddr *)&(p->cin), sizeof(p->cin));

}

p=p->next; //后移

}

printf("%s:%s\n",msg.name, msg.text);

}

//用户聊天操作函数

void usr_chat(int sfd,msg_t msg,addrlist head,struct sockaddr_in cin)

{

addrlist p=head;

while (p!=NULL)

{

if(p->cin.sin_port!=cin.sin_port)

{

sendto(sfd, &msg, sizeof(msg),0,(struct sockaddr *)&(p->cin), sizeof(p->cin));

}

p=p->next; //后移

}

}

//用户退出操作函数

void usr_quit(int sfd,msg_t msg,addrlist *head,struct sockaddr_in cin)

{

printf("%s:%s\n",msg.name, msg.text);

addrlist p=*head;

addrlist del=NULL;

while (p!=NULL)

{

if(p->cin.sin_port!=cin.sin_port) //向其他用户发送某个用户下线消息

{

sendto(sfd, &msg, sizeof(msg),0,(struct sockaddr *)&(p->cin), sizeof(p->cin));

del=p;

p=p->next;

}

else

{

sendto(sfd, &msg, sizeof(msg),0,(struct sockaddr *)&(p->cin), sizeof(p->cin));//向发出下线消息的用户回复消息

if (del==NULL)

{

*head=p->next;

}

else

{

del->next=p->next;

}

free(del);

del=NULL;

break;

}

}

}

客户端:

#include

//定义客户信息结构体

typedef struct magtye

{

char type; //消息类型

char name[100]; //客户姓名

char text[1024]; //客户发送聊天信息

}msg_t;

int main(int argc, const char *argv[])

{

//创建套接字

int cfd=-1;

cfd=socket(AF_INET, SOCK_DGRAM, 0);

if(cfd==-1)

{

perror("socket error");

return -1;

}

//将端口号快速重用

int reuse=1;

if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)

{

perror("setsockopt error");

return -1;

}

//(1)、从终端获取端口号和地址

char SER_IP[100];

int SER_PORT;

printf("请输入服务器ip地址和端口号:");

scanf("%s %d",SER_IP,&SER_PORT);

getchar();//吸收垃圾字符

//(2)、填充服务器地址信息结构体

struct sockaddr_in sin;

sin.sin_family=AF_INET; //地址族

sin.sin_port=htons(SER_PORT); //端口号

sin.sin_addr.s_addr=inet_addr(SER_IP); //IP地址

socklen_t sin_len=sizeof(sin);

msg_t msg;

//客户端上线发送消息

printf("请输入用户名:");

fgets(msg.name,sizeof(msg.name),stdin);

msg.name[strlen(msg.name)-1] = '\0';

strcpy(msg.text,"已上线");

msg.type='L';

sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len);

//创建多进程进行收发消息操作

int pid=fork();

if(pid==-1)

{

perror("fork error");

return -1;

}

if(pid>0)//父进程进行读取消息

{

while(1)

{

recvfrom(cfd,&msg,sizeof(msg),0,NULL,NULL);

if(strcmp(msg.text,"退出群聊")==0) //用户自己下线

{

break;

}

printf("[%s]: %s\n", msg.name, msg.text);

if(strcmp(msg.text,"服务器下线")==0) //服务器让客户端下线

{

kill(pid,SIGKILL);

break;

}

}

}

else if(pid==0)//子进程发送消息

{

while(1)

{

memset(msg.text,0,sizeof(msg.text));

fgets(msg.text,sizeof(msg.text),stdin);//在终端获取聊天信息

msg.text[strlen(msg.text)-1]='\0';

if(strcmp(msg.text, "下线")==0)

{

msg.type='Q';

strcpy(msg.text, "退出群聊");

}

else

{

msg.type='C';

}

sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len);

if(strcmp(msg.text,"退出群聊")==0)

{

break;

}

}

exit(EXIT_SUCCESS);

}

//关闭套接字回收子进程资源

wait(NULL); //阻塞回收子进程资源

close(cfd);

return 0;

}

思维导图:

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: