一、UDP网络编程———准备阶段
1.1项目要求
利用UDP协议,实现一套聊天室软件。服务器端记录客户端的地址,客户端发送消息后,服务器群发给各个客户端软件。
1.2实现功能
登录:服务器存储新的客户端的地址。把某个客户端登录的消息发给其它客户端。
聊天:服务器只需要把某个客户端的聊天消息转发给所有其它客户端。
退出:服务器删除退出客户端的地址,并把退出消息发送给其它客户端。
实现思路:
(1) 服务器存储客户端的地址可以采用:数据结构可以选择线性数据结构。
typedef struct { char type; //消息类型 L C Q char id[32]; //用户id char text[128]; //消息内容 } msg_t; //链表节点 typedef struct node_t { struct sockaddr_in caddr; struct node_t *next; } list;
(2) 客户端如何同时处理发送和接收的问题可以使用多任务来同时处理,使用多进程或者多线程来处理。
1.3程序流程图
二、代码实现(源码)
2.1服务器端 seaver.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include<pthread.h>
typedef struct
{
char type; //消息类型 L C Q
char id[32]; //用户id
char text[128]; //消息内容
} msg_t;
//链表节点
typedef struct node_t
{
struct sockaddr_in caddr;
struct node_t *next;
} list;
struct sockaddr_in saddr, caddr;
//创建头节点
list *list_create(void)
{
list *p = (list *)malloc(sizeof(list));
if (p == NULL)
{
perror("malloc err.\n");
return NULL;
}
p->next = NULL;
return p;
}
void login(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr);
void chat(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr);
void quit(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr);
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("usage:./a.out <port>\n");
return -1;
}
int sockfd;
struct sockaddr_in saddr, caddr;
socklen_t len = sizeof(caddr);
msg_t msg;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("sock err.\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
saddr.sin_port = htons(atoi(argv[1]));
bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
printf("bind ok!\n");
list *p = list_create();
int recvbyte;
pthread_t tid;
pthread_create(&tid,NULL,handler,&sockfd);
pthread_detach(tid);
while (1)
{
struct timeval val={2,0};
setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&val,sizeof(val));
recvbyte = recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len);
if (recvbyte < 0)
{
perror("recvfrom err.\n");
return -1;
}
if (msg.type == 'L') //登入
{
login(sockfd, msg, p, caddr);
}
else if (msg.type == 'C') //聊天
{
chat(sockfd, msg, p, caddr);
}
else if (msg.type == 'Q') //退出
{
printf("type :%c\n", msg.type);
printf("ip:%s pord:%d id:%s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), msg.id);
printf("text:%s\n", msg.text);
quit(sockfd, msg, p, caddr);
}
}
close(sockfd);
return 0;
}
void login(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr)
{
list *new = NULL;
sprintf(msg.text, "login");
while (p->next != NULL)
{
p = p->next; //发送给的其他用户 登入消息
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
printf("%s\n", msg.text);
}
//将新用户信息保存 尾插到链表
new = (list *)malloc(sizeof(list));
new->caddr = caddr;
new->next = NULL;
p->next = new;
return;
}
void chat(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr)
{
int n;
while (p->next != NULL)
{
p = p->next;
n = memcmp(&(p->caddr), &caddr, sizeof(caddr));
if (n != 0)
{
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
}
}
return;
}
void quit(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr)
{
list *dele;
sprintf(msg.text, "logout");
while (p->next != NULL)
{
if ((memcmp(&(p->next->caddr), &caddr, sizeof(caddr))) == 0) //找要释放的前一个节点
{
dele = p->next;
p->next = dele->next;
free(dele);
dele = NULL;
}
else
{
p = p->next;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
}
}
return;
}
void * handler(void *arg)
{
int socketfd = *(int *)arg;
msg_t msg_s;
strcpy(msg_s.id,"seaver");
while (1)
{
scanf("%[^\n]s",msg_s);
getchar();
sendto(socketfd,msg_s.text,sizeof(msg_s),0,(struct sockaddr *)caddr,sizeof(caddr));
}
}
2.2用户端 client.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
typedef struct
{
char type; //消息类型 L C Q
char id[32]; //用户id
char text[128]; //消息内容
} msg_t;
int main(int argc, char const *argv[])
{
if (argc != 3)
{
printf("usage:./a.out <ip> <port> \n");
return -1;
}
int sockfd;
msg_t msg;
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
char buf[128];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("sock err.\n");
return -1;
}
caddr.sin_family = AF_INET;
caddr.sin_addr.s_addr = inet_addr(argv[1]);
caddr.sin_port = htons(atoi(argv[2]));
msg.type = 'L';
printf("please imput your id\n");
scanf("%[^\n]s", msg.id);
getchar();
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, len);
pid_t pid = fork();
if (pid < 0)
{
perror("fork err.\n");
return -1;
}
else if (pid == 0) //子进程循环发送消息
{
while (1)
{
scanf("%[^\n]s", msg.text);
getchar();
//printf("%s\n", msg.text);
if (strncmp(msg.text, "quit", 4) == 0)
{
msg.type = 'Q';
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, len);
kill(getppid(), SIGKILL);
wait(NULL);
exit(-1);
}
else
{
msg.type = 'C';
}
//printf("sssssshu\n");
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, len);
}
}
else //父进程循环接受消息
{
int recvbyte;
while (1)
{
recvbyte = recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL);
if (recvbyte < 0)
{
perror("recvfrom err.\n");
return -1;
}
printf("%s:%s\n", msg.id, msg.text);
}
wait(NULL);
}
close(sockfd);
return 0;
}
2.3运行结果
文章来源:https://uudwc.com/A/db161
文章来源地址https://uudwc.com/A/db161