要求:
项目需求:
如果有用户登录,其他用户可以收到这个人的登录信息
如果有人发送信息,其他用户可以收到这个人的群聊信息
如果有人下线,其他用户可以收到这个人的下线信息
服务器可以发送系统
客户端:
ubuntu@ubuntu:day6$ cat 2udpCli.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d\n", __LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.5.21"
#define PORT 6677
//存储协议
struct msg
{
char type;
char name[20];
char text[128];
};
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//绑定客户端自身的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
char buf[128] = "";
int nbytes = 0;
ssize_t res = 0;
pid_t pid;
struct sockaddr_in rcvAddr; //存储接收到的数据包的地址信息
socklen_t addrlen = sizeof(rcvAddr);
struct msg mg;
//输入登录姓名
mg.type = 'L';
char name[20]="";
printf("请输入姓名>>>");
scanf("%s",name);
while(getchar()!=10);
strcpy(mg.name,name);
//发送数据包
if(sendto(sfd, &mg, sizeof(mg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//创建子进程
pid = fork();
if(pid>0)
{
//群聊、下线信息
//发送群聊数据包
while(1)
{
bzero(mg.text,sizeof(mg));
scanf("%s",mg.text);
while(getchar()!=10);
//下线
if(strcmp(mg.text,"quit")==0)
{
mg.type = 'Q';
if(sendto(sfd,&mg,sizeof(mg),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
break;
}
else
{
mg.type = 'C';
}
if(sendto(sfd, &mg, sizeof(mg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
exit(0);
}
//子进程 接受服务器信息
if(pid==0)
{
while (1)
{
memset(&mg,0,sizeof(mg));
//接收服务器的应答
if (-1 == (nbytes=recvfrom(sfd, &mg, sizeof(mg), 0,(struct sockaddr *)&rcvAddr,&addrlen)))
{
ERR_MSG("recv error");
}
if(strcmp(mg.name,name) == -10){
continue;
}
else
{
//打印应答信息
switch(mg.type){
case 'L':
printf("[%s]登录上线了....\n", mg.name);
break;
case 'C':
printf("[%s]:%s\n",mg.name,mg.text);
break;
case 'Q':
printf("[%s]退出了....\n", mg.name);
break;
}
}
}
//关闭套接字
close(sfd);
return 0;
}
}
服务器代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<signal.h>
#include<wait.h>
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d\n", __LINE__);\
perror(msg);\
}while(0)
#define M 20
#define N 128
typedef struct _Node{
struct sockaddr_in addr;
struct _Node *next;
}node_t;
typedef struct _Msg{
char type;
char name[M];
char text[N];
}msg_t;
//创建节点的函数
int create_node(node_t **phead){
*phead = (node_t *)malloc(sizeof(node_t));
if(NULL == *phead){
printf("内存分配失败\n");
exit(-1);
}
(*phead)->next=NULL;
return 0;
}
//尾插法
int insert_data_by_tail(node_t *phead,struct sockaddr_in addr){
if(NULL == phead){
printf("为空\n");
return -1;
}
//将新客户端使用尾插法插入链表中
node_t *pnew = NULL;
create_node(&pnew);
pnew->addr = addr;
node_t *ptemp =phead;
while(ptemp->next != NULL){
ptemp = ptemp->next;
}
//让尾结点的指针域指向新节点
ptemp->next = pnew;
return 0;
}
int main(int argc,const char *argv[]){
if(3 != argc){
printf("Uage:%s <IP><port>\n",argv[0]);
return -1;
}
int sockfd = 0;
if(-1==(sockfd=socket(AF_INET,SOCK_DGRAM,0))){
ERR_MSG("socket error");
}
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);
if(-1 == bind(sockfd,(struct sockaddr *)&serveraddr,serveraddr_len)){
ERR_MSG("bind error");
}
struct sockaddr_in clientaddr,temp_clientaddr;
memset(&clientaddr,0,sizeof(clientaddr));
socklen_t clientaddr_len = sizeof(clientaddr);
char name[32] = {0};
pid_t pid = 0;
msg_t msg;
msg_t msg_send;
//创建头结点
node_t *phead;
create_node(&phead);
phead->addr = clientaddr;
if(-1 == (pid = fork())){
ERR_MSG("fork error");
}else if(0 == pid){
while(1){
memset(&msg,0,sizeof(msg));
if(-1 == recvfrom(sockfd, (void*)&msg, sizeof(msg),0, (struct sockaddr *)&clientaddr,&clientaddr_len)){
perror("recv error");
}
switch(msg.type){
case 'L':
printf("[%s]该玩家已上线\n", msg.name);
insert_data_by_tail(phead,clientaddr);
node_t *q=phead->next;
while(q != NULL){
msg.type='L';
if(-1 == sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&q->addr,sizeof(q->addr))){
ERR_MSG("send error");
}
q=q->next;
}
break;
case 'C':
if(strcmp("管理员",msg.name)!=0){
printf("[%s]:%s\n",msg.name, msg.text);
}
node_t *p = phead->next;
while(p != NULL){
msg.type='C';
if(-1 == sendto(sockfd,(void *)&msg,sizeof(msg),0,(struct sockaddr *)&p->addr,sizeof(p->addr))){
ERR_MSG("send error");
}
p=p->next;
}
break;
case 'Q':
printf("[%s]:退出了...\n", msg.name);
node_t *t = phead;
node_t *pdel = NULL;
while(t->next != NULL){
msg.type='Q';
if( 0 == memcmp(&(t->next->addr), &clientaddr,sizeof(clientaddr))){
pdel = t->next;
t->next = pdel->next;
free(pdel);
}else{
t = t->next;
if(-1 == sendto(sockfd, &msg,sizeof(msg),0,(struct sockaddr *)&t->addr,sizeof(t->addr))){
ERR_MSG("send error");
}
}
}
break;
}
}
}else if(0 < pid){
//父进程 发送系统消息
while(1){
strcpy(msg_send.name,"管理员");
memset(msg_send.text,0,N);
fgets(msg_send.text,N,stdin);
msg_send.text[strlen(msg_send.text)-1] = '\0';
msg_send.type = 'C';
if(-1 == sendto(sockfd,&msg_send,sizeof(msg_send),0,(struct sockaddr *)&serveraddr,serveraddr_len)){
ERR_MSG("send error");
}
}
}
kill(pid, SIGKILL);
wait(NULL);//给子进程回收资源
exit(0);
close(sockfd);
return 0;
}
结果如下: