需求分析
网络协议: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 ) ;
}