需求分析
- 网络协议:UDP
- 服务器需求:
- 需要一个存放用户数据的容器
- 服务器需要区分用户的操作类型(如:上线,下线,发送消息)
- 因为存在多个阻塞IO,需要多进程,多线程,或IO复用实现
- 客户端需求:
- 客户端发送的消息,需要带有相应的消息类型
- 能够接收服务器转发消息,并区分出消息发送者
消息协议及其核心函数
typedef struct msg
{unsigned short type;unsigned char name[32];unsigned char data[128];
} Msg;
int Conn_Event(int server, Msg msg, ClistPtr C, struct sockaddr_in caddr);
int Data_Event(int server, Msg msg, ClistPtr C, struct sockaddr_in caddr);
int Close_Event(int server, Msg msg, ClistPtr C, struct sockaddr_in caddr);
登录事件核心功能实现
while(p->next != NULL)
{p = p->next;if(sendto(server, &msg, sizeof(msg), 0, (struct sockaddr*)&(p->caddr), sizeof(p->caddr)) < 0){LOG("sendto error");return -1;}}
list_insert_head(C, caddr);
数据事件核心功能实现
while(C->next != NULL)
{C = C->next;if(memcmp(&(C->caddr), &caddr, sizeof(caddr)) != 0){sendto(server, &msg, sizeof(msg), 0, (struct sockaddr*)&(C->caddr), sizeof(C->caddr));}
}
断连事件核心功能实现
if(NULL == C->next)
{printf("最后一位用户%s已下线\n", msg.name);return 0;
}while(p->next != NULL)
{if(memcmp(&(p->next->caddr), &caddr, sizeof(caddr)) == 0){list_delete(p);}else{p = p->next;if(sendto(server, &msg, sizeof(msg), 0, (struct sockaddr*)&p->caddr, sizeof(p->caddr)) < 0){LOG("sendto error");return -1;} }
}
服务器函数
#include "udp_sever.h"#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);
typedef struct
{int server;Msg msg;ClistPtr C;struct sockaddr_in caddr;} Arg_t;
void* forward(void* arg)
{int server = ((Arg_t*)arg)->server;Msg msg = ((Arg_t*)arg)->msg;ClistPtr C = ((Arg_t*)arg)->C;struct sockaddr_in caddr = ((Arg_t*)arg)->caddr;Data_Event(server, msg, C, caddr);pthread_exit(NULL);
}int main(int argc, char const *argv[])
{struct sockaddr_in saddr = {0};ClistPtr C = list_create();if(NULL == C){LOG("list_creat error");return -1;}if(argc != 2){printf("please inputs: %s <port>\n", argv[0]);return -1;}int server = 0;if((server = socket(AF_INET, SOCK_DGRAM, 0)) < 0){LOG("server socket error");return -1;}saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){LOG("bind error");return -1;}printf("udp server start success\n");int res = -1;Msg msg;struct sockaddr_in caddr = {0};socklen_t len = sizeof(caddr);pthread_t tid;Arg_t arg;while(1){if((res = recvfrom(server, &msg, sizeof(msg), 0, (struct sockaddr*)&caddr, &len)) < 0){LOG("recvfrom error");return -1;}switch(msg.type){case EVE_CONN:Conn_Event(server, msg, C, caddr);break;case EVE_DATA:arg.server = server;arg.msg = msg;arg.caddr = caddr;arg.C = C;if(pthread_create(&tid, NULL, forward, &arg) < 0){LOG("pthread_create error");return -1;}pthread_detach(tid);break;case EVE_CLOSE:Close_Event(server, msg, C, caddr);break;default:LOG("event error");return -1;}}close(server);return 0;
}
客户端函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "udp_sever.h"#define LOG(s) printf("[%s] {%s:%d} %s\n", __DATE__, __FILE__, __LINE__, s);
Msg msg;
int sock = 0;
struct sockaddr_in remote = {0};void handler(int sig);int main(int argc, char const *argv[])
{if(argc != 4){printf("please inputs: %s <user name> <host port> <server ip> <server port>\n", argv[0]);return -1;}struct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));addr.sin_addr.s_addr = htonl(INADDR_ANY);fd_set reads = {0};fd_set temps = {0};sock = socket(AF_INET, SOCK_DGRAM, 0);if( sock == -1 ){LOG("socket error");return -1;}signal(SIGINT, handler);remote.sin_family = AF_INET;remote.sin_addr.s_addr = inet_addr(argv[3]);remote.sin_port = htons(atoi(argv[4]));msg.type = EVE_CONN;stpcpy(msg.name, argv[1]);if(sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&remote, sizeof(remote)) < 0){LOG("connect failed");return -1;}printf("connect success\n");FD_ZERO(&reads);FD_SET(sock, &reads);FD_SET(0, &reads);Msg rmsg;while(1){msg.type = EVE_DATA;temps = reads;int num = select(sock+1, &temps, NULL, NULL, NULL);if(num > 0){if(FD_ISSET(0, &temps)){bzero(msg.data, sizeof(msg.data));fgets(msg.data, sizeof(msg.data), stdin);msg.data[strlen(msg.data)-1] = 0;if(sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&remote, sizeof(remote)) < 0){LOG("sendto error");return -1;}}else{int res = -1;if((res = recvfrom(sock, &(rmsg), sizeof(rmsg), 0, NULL, NULL)) < 0){LOG("recvfrom error");return -1;}else if(0 == res){printf("server close \n");return -1;}printf("[%s]: %s\n", rmsg.name, rmsg.data);} } }close(sock);return 0;
}void handler(int sig)
{msg.type = EVE_CLOSE;sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&remote, sizeof(remote));exit(-1);
}