2023-5-22
搭建UDP聊天服务器端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#define ERR_LOG(msg) do{printf("%d %s %s \n",__LINE__,__func__,__FILE__);perror(msg);return -1;}while(0)
//协议
#define LOGIN 1
#define CHAT 2
#define QUIT 3
#define N 128
//收发消息
typedef struct
{
int type;
char name[20];
char text[N];
}MSG;
typedef struct node
{
struct sockaddr_in cin;
struct node*next;
}__linklist;
//函数声明
int do_login(int sfd,__linklist*head,MSG rcv_msg,struct sockaddr_in cin);
int do_quit(int sfd,__linklist*head,MSG rcv__msg,struct sockaddr_in cin);
int do_chat(int sfd,__linklist* head,MSG rcv_msg,struct sockaddr_in cin);
int do_recv(int sfd,__linklist*head);
int do_system(int sfd,struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
if(argc<3)
{printf("Enter IP and port number!\n");return -1;}
//创建报式套接子
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0){perror("socket");return -1;}
//绑定IP和端口号
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]);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{perror("bind");return -1;}
//创建进程
int pid=0;
pid=fork();
if(pid>0)
{
//父进程,接收消息
__linklist*head=(__linklist*)malloc(sizeof(__linklist));
head->next=NULL;
do_recv(sfd,head);
}
else if(pid==0)
{
//子进程发消息
do_system(sfd,sin);
}
//关闭套接字
if(close(sfd)<0)
{perror("close");return -1;}
return 0;
}
int do_system(int sfd,struct sockaddr_in sin)
{
MSG sys_msg={htonl(CHAT),"**system**"};
while(1)
{
bzero(sys_msg.text,N);
fgets(sys_msg.text,N,stdin);
sys_msg.text[strlen(sys_msg.text)-1]=0;
//当前进程做客户端发消息;
if(sendto(sfd,&sys_msg,sizeof(sys_msg),0,(void*)&sin,sizeof(sin))<0)
{ERR_LOG("sendto");return -1;}
}
printf("The system message was sent successfully");
return 0;
}
int do_recv(int sfd,__linklist*head)
{
MSG rcv_msg;
int recv_len=0;
struct sockaddr_in cin;
socklen_t clen=sizeof(cin);
while(1)
{
//接收消息
recv_len=recvfrom(sfd,&rcv_msg,sizeof(rcv_msg),0,(void*)&cin,&clen);
if(recv_len<0)
{perror("recvrom");return -1;}
//网转主机字节序
int type=ntohl(rcv_msg.type);
switch(type)
{
case LOGIN:
do_login(sfd,head,rcv_msg,cin);
break;
case CHAT:
do_chat(sfd,head,rcv_msg,cin);
break;
case QUIT:
do_quit(sfd,head,rcv_msg,cin);
break;
}
}
}
int do_chat(int sfd,__linklist* head,MSG rcv_msg,struct sockaddr_in cin)
{
printf("%s[%s:%d] Chat success!\n",rcv_msg.name,(char*)inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
char buf[258]="";
sprintf(buf,"%s:%s",rcv_msg.name,rcv_msg.text);
strcpy(rcv_msg.text,buf);
//打印其他IP地址
while(head->next !=NULL)
{
head=head->next;
if(memcmp(&cin,&head->cin,sizeof(cin))!=0)
{
if(sendto(sfd,&rcv_msg,sizeof(rcv_msg),0,(void*)&(head->cin),sizeof(head->cin))<0)
{
ERR_LOG("sendto");return -1;
}
}
}
return 0;
}
int do_quit(int sfd,__linklist*head,MSG rcv_msg,struct sockaddr_in cin)
{
sprintf(rcv_msg.text,">>>> %sOffline <<<<\n",rcv_msg.name);
//群发其它客户端下线消息
while(head->next!=NULL)
{
if(memcmp(&cin,&head->next->cin,sizeof(cin))==0)
{
//删除登陆客户信息
__linklist*temp=head->next;
head->next=temp->next;
free(temp);
}
else
{
head=head->next;
if(sendto(sfd,&rcv_msg,sizeof(rcv_msg),0,(void*)&head->cin,sizeof(head->cin))<0)
{
ERR_LOG("sendto");
return -1;
}
}
}
return 0;
}
int do_login(int sfd,__linklist*head,MSG rcv_msg,struct sockaddr_in cin)
{
printf("%s[%s:%d] Login success!\n",rcv_msg.name,(char*)inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
sprintf(rcv_msg.text," >>>>%sLogin success!",rcv_msg.name);
while(head->next !=NULL)
{
head=head->next;
if(sendto(sfd,&rcv_msg,sizeof(rcv_msg),0,(void*)&(head->cin),sizeof(head->cin))<0)
{
ERR_LOG("sendto");return -1;
}
}
//添加连接客户信息
__linklist*temp=(__linklist*)malloc(sizeof(__linklist));
temp->cin=cin;
temp->next=NULL;
head->next=temp;
return 0;
}