网络聊天室
1.服务器
#include
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
typedef struct msg{
char type; //操作码'L'登录 'C'群聊 'Q'下线
char name[20];
char text[128];
}msg_t;
typedef struct ADDR{
struct sockaddr_in clientaddr;
struct ADDR* next;
}addrlist_t;
void do_login(int sfd, msg_t msg, addrlist_t*addr,struct sockaddr_in clientaddr);
void do_chat(int sfd, msg_t msg, addrlist_t*addr,struct sockaddr_in clientaddr);
void do_quit(int sfd, msg_t msg, addrlist_t*addr,struct sockaddr_in clientaddr);
/******************************************************************************/
int main(int argc, const char *argv[])
{
if(3 != argc)
{
printf("input error\n");
printf("usage: %s \n",argv[0]);
return -1;
}
// socket函数
int sfd;
if((sfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
{
ERR_MSG("socket");
return -1;
}
//填充服务器网络信息结构体
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t serveraddr_len = sizeof(serveraddr);
//将套接字绑定网络信息 bind函数
if(bind(sfd, (struct sockaddr*)&serveraddr, serveraddr_len)<0)
{
ERR_MSG("bind");
return -1;
}
//定义客户端网络信息结构体
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
msg_t msg;
pid_t pid; //进程号
if((pid = fork())<0)
{
ERR_MSG("fork");
return -1;
}
if(0 == pid) //子进程,用来发送系统信息
{
strcpy(msg.name,"系统消息");
msg.type = 'C';
while(1)
{
memset(msg.text,0,sizeof(msg.text));
fgets(msg.text, 128, stdin);
msg.text[strlen(msg.text)-1] = '\0';
if(sendto(sfd, &msg, sizeof(msg),0,\
(struct sockaddr*)&serveraddr, serveraddr_len)<0)
{
ERR_MSG("sendto");
return -1;
}
}
}
else if(0 < pid) //父进程,用来收发数据
{
//创建头结点
addrlist_t* addr = (addrlist_t*)malloc(sizeof(addrlist_t));
if(NULL==addr)
{
printf("malloc error\n");
return -1;
}
memset(addr, 0, sizeof(addr));
addr->next = NULL;
//循环收发数据
while(1)
{
//接收新数据及新用户前清空
memset(&msg, 0, sizeof(msg));
memset(&clientaddr, 0, sizeof(clientaddr));
if(recvfrom(sfd, &msg, sizeof(msg), 0,\
(struct sockaddr*)&clientaddr, &clientaddr_len)<0)
{
ERR_MSG("recvfrom");
return -1;
}
//判断消息中的错误码,执行对应操作
switch(msg.type)
{
case 'L': //登录操作
do_login(sfd, msg,addr, clientaddr);
break;
case 'C': //群聊请求
do_chat(sfd, msg,addr, clientaddr);
break;
case 'Q': //下线请求
do_quit(sfd, msg,addr, clientaddr);
break;
}
}
}
if(close(sfd)<0)
{
ERR_MSG("close");
return -1;
}
return 0;
}
//登录操作的函数
void do_login(int sfd, msg_t msg, addrlist_t* addr,struct sockaddr_in clientaddr)
{
//遍历链表,将新用户加入群聊的消息发送给所有人
addrlist_t *temp = addr; //记录链表头结点
while(temp->next != NULL)
{
temp = temp->next;
if(sendto(sfd, &msg, sizeof(msg), 0, \
(struct sockaddr*)&(temp->clientaddr), sizeof(temp->clientaddr))<0)
{
ERR_MSG("sendto");
return ;
}
}
//将新用户的网络结构体,头插入链表
addrlist_t* new = (addrlist_t*)malloc(sizeof(addrlist_t));
if(NULL == new)
{
printf("malloc error\n");
return ;
}
new->clientaddr = clientaddr;
new->next = addr->next;
addr->next = new;
return;
}
//群聊函数
void do_chat(int sfd, msg_t msg, addrlist_t*addr,struct sockaddr_in clientaddr)
{
//遍历链表,将群消息发送给除了自己外的所有人
addrlist_t* temp = addr;
while(temp->next != NULL)
{
temp = temp->next;
if(memcmp(&clientaddr, &(temp->clientaddr),sizeof(clientaddr)))
{
if(sendto(sfd, &msg,sizeof(msg), 0, \
(struct sockaddr*)&(temp->clientaddr),sizeof(temp->clientaddr))<0)
{
ERR_MSG("sendto");
return ;
}
}
}
return ;
}
//退出聊天
void do_quit(int sfd, msg_t msg, addrlist_t*addr, struct sockaddr_in clientaddr)
{
//遍历链表,是自己就删除
//不是自己,就发送退出群聊的消息
addrlist_t* temp = addr;
addrlist_t* del = NULL;
while(temp->next != NULL)
{
if(memcmp(&(temp->next->clientaddr),&clientaddr,sizeof(clientaddr)))
{
//不是自己
temp = temp->next;
if(sendto(sfd, &msg, sizeof(msg), 0, \
(struct sockaddr*)&(temp->clientaddr), sizeof(temp->clientaddr))<0)
{
ERR_MSG("sendto");
return ;
}
}else
{
//自己
del = temp->next;
temp->next = del->next;
free(del);
del = NULL;
}
}
return ;
}
2.客户端
#include
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
typedef struct msg{
char type; //操作码'L'登录 'C'群聊 'Q'下线
char name[20];
char text[128];
}msg_t;
/************************************************/
int main(int argc, const char *argv[])
{
if(3 != argc)
{
printf("input error\n");
printf("usage: %s \n",argv[0]);
return -1;
}
// socket函数
int cfd;
if((cfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
{
ERR_MSG("socket");
return -1;
}
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t serveraddr_len = sizeof(serveraddr);
msg_t msg;
memset(&msg, 0, sizeof(msg));
printf("请输入用户名:");
fgets(msg.name, 20, stdin);
msg.name[strlen(msg.name)-1] = '\0';
msg.type = 'L';
strcpy(msg.text, "加入群聊");
//给服务器发送登录信息
if(sendto(cfd,&msg, sizeof(msg), 0, \
(struct sockaddr*)&serveraddr, serveraddr_len)<0)
{
ERR_MSG("sendto");
return -1;
}
pid_t pid = 0;
if((pid = fork())<0)
{
ERR_MSG("fork");
return -1;
}
if(0 == pid) //子进程循环在终端接收数据
{
while(1){
memset(msg.text,0, sizeof(msg.text));
fgets(msg.text, 128, stdin); //在终端获取聊天信息
msg.text[strlen(msg.text)-1] = '\0';
if(!strcmp(msg.text,"quit"))
{
msg.type = 'Q';
strcpy(msg.text, "退出群聊");
}else
{
msg.type = 'C';
}
if(sendto(cfd,&msg, sizeof(msg), 0, \
(struct sockaddr*)&serveraddr, serveraddr_len)<0)
{
ERR_MSG("sendto");
return -1;
}
if(!strcmp(msg.text,"退出群聊")){
break;
}
}
}
else if(0 < pid) //父进程,循换接收并打印接收到的数据
{
while(1)
{
if(recvfrom(cfd, &msg, sizeof(msg), 0, NULL,NULL)<0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("[%s]:%s\n",msg.name, msg.text); //打印收到的数据
}
kill(pid, SIGKILL);
wait(NULL);
}
close(cfd);
return 0;
}