胡漢三66 的學(xué)生作業(yè):
udp_server.c
#include /* See NOTES */
#include // socket() sendto()
#include
#include // inet_addr() htons()
#include // memset()
#include // fgets()
#include // close()
#include // exit()
#include // signal()
#include // waitpid()
#include // pthread_create()
#define LOGIN_SUCCESS 1 // 宏定義 登錄成功 1
#define LOGIN_FAILURE 0 // 宏定義 登錄失敗 0
typedef struct{ // 線程參數(shù)結(jié)構(gòu)體
char *ip; // IP
unsigned char flag; // 標(biāo)記
struct sockaddr_in *peer_addr; // 目標(biāo)地址 結(jié)構(gòu)體
}thread_type;
void recv_data(int new_sockfd){ // 接收數(shù)據(jù)
int n = 0;
char buf[1024] = {0};
struct sockaddr_in client_addr; // 客戶端地址
int len = sizeof(client_addr); // 客戶端地址大小
while(1){
memset(buf,0,sizeof(buf)); // 重置 buf空間為 0
n = recvfrom(new_sockfd,buf,sizeof(buf),0,(struct sockaddr *)&client_addr,&len); // 接收信息
if(n < 0){
perror("Fail to recvfrom");
exit(EXIT_FAILURE);
}
printf("================================\n");
printf("Recv from IP = %s\n", inet_ntoa(client_addr.sin_addr)); // 打印 客戶端 IP
printf("Recv from Port = %d\n", ntohs(client_addr.sin_port)); // 打印 客戶端 端口
printf("Recv %d bytes : %s\n",n,buf); // 打印 接收的文件大小 接收的內(nèi)容
if(strncmp(buf,"quit",4) == 0) // 如果輸入 "quit" 退出程序
break;
}
close(new_sockfd);
return ;
}
int init_socket(const char *ip,const char *port){ // 創(chuàng)建 子線程 套接字的 文件描述符
int sockfd;
struct sockaddr_in my_addr; // 目標(biāo) 地址結(jié)構(gòu)體
int len = sizeof(my_addr);
// 1.通過socket創(chuàng)建文件描述符
sockfd = socket(AF_INET,SOCK_DGRAM,0); // 子進(jìn)程的 文件描述符
if(sockfd < 0){
perror("Fail to socket!");
exit(EXIT_FAILURE);
}
// 2.填充服務(wù)器的ip + port
memset(&my_addr,0,sizeof(my_addr)); // 重置 my_addr空間為 0
my_addr.sin_family = AF_INET; // 設(shè)置 協(xié)議族(IP4)
my_addr.sin_port = htons(atoi(port)); // 設(shè)置 目標(biāo)網(wǎng)絡(luò)端口 // port為0,系統(tǒng) 自動生成 一個端口
my_addr.sin_addr.s_addr = inet_addr(ip); // 設(shè)置 目標(biāo)網(wǎng)絡(luò)IP
//3.把ip + port與socket綁定
if(bind(sockfd,(struct sockaddr *)&my_addr,len) < 0){
perror("Fail to bind");
exit(EXIT_FAILURE);
}
printf("wait recv data!\n");
return sockfd; // 返回 子進(jìn)程 套接字的 文件描述符.
}
void *message_thread(void *arg){
thread_type *packet = (thread_type *)arg; // 線程參數(shù) 結(jié)構(gòu)體
int new_sockfd; // 保存 子線程的 套接字 文件描述符
// 1.給用戶 回復(fù)的 秘鑰正確信息
char *ip = packet->ip; // ip
unsigned char login_flag = packet->flag; // 標(biāo)記
struct sockaddr_in *addr = packet->peer_addr; // 客戶端地址 結(jié)構(gòu)體
new_sockfd = init_socket(ip,"0"); // 創(chuàng)建 新的套接字 和 端口 準(zhǔn)備用于通信
sendto(new_sockfd,&login_flag,sizeof(login_flag),0,(struct sockaddr *)addr,sizeof(struct sockaddr)); // 接收信息
// 2.接收數(shù)據(jù)
recv_data(new_sockfd);
pthread_exit(NULL); // 退出線程
}
void user_login(const char *ip,const char *port){ // 用戶登錄, 返回 子進(jìn)程 套接字文件描述符
int sockfd;
int n = 0;
char buf[20] = {0};
struct sockaddr_in client_addr; // 客戶端地址
int len = sizeof(client_addr); // 客戶端地址大小
unsigned char login_flag; // 登錄標(biāo)記
pthread_t tid; // 線程 ID
thread_type packet; // 線程參數(shù) 結(jié)構(gòu)體
// 1.創(chuàng)建套接字
sockfd = init_socket(ip,port);
// 2.接收登錄信息, 父進(jìn)程負(fù)責(zé)驗(yàn)證秘匙信息, 子進(jìn)程負(fù)責(zé)與用戶進(jìn)行數(shù)據(jù)交互.
while(1){
memset(buf,0,sizeof(buf)); // 重置 buf空間為 0
n = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&client_addr,&len); // 接收信息
if(n < 0){
perror("Fail to recvfrom");
exit(EXIT_FAILURE);
}
printf("key = %s\n",buf);
login_flag = (strncmp(buf,"root",4) == 0) ? LOGIN_SUCCESS : LOGIN_FAILURE; // 如果輸入 密碼正確為root, 返回標(biāo)記為1
// 此時說明秘鑰正確
if(login_flag == LOGIN_SUCCESS) // 如果 登錄成功
{
packet.flag = login_flag; // 標(biāo)記
packet.ip = (char *)ip; // 服務(wù)端IP
packet.peer_addr = &client_addr; // 客戶端地址 結(jié)構(gòu)體
// 創(chuàng)建子線程,子線程準(zhǔn)備接收當(dāng)前用戶的數(shù)據(jù) // 主線程負(fù)責(zé)接收用戶的秘鑰
pthread_create(&tid,NULL,message_thread,(void *)&packet); // 創(chuàng)建子線程
}else{ // 如果 登錄失敗
// 用父進(jìn)程套接字 給客戶端回復(fù)---登錄失敗
sendto(sockfd,&login_flag,sizeof(login_flag),0,(struct sockaddr *)&client_addr,len); // 接收信息
}
pthread_detach(tid); // 把線程設(shè)置為分離式,線程結(jié)束后,系統(tǒng)會自動回收資源
}
return ;
}
// ./a.out ip port
int main(int argc, const char *argv[])
{
int sockfd;
if(argc != 3){ // 判斷 輸入數(shù)據(jù) 是否有3個
fprintf(stderr,"Usage : %s ip port!\n",argv[0]);
exit(EXIT_FAILURE);
}
// 1.接收用戶的秘鑰, 準(zhǔn)備登錄
user_login(argv[1],argv[2]);
// 2. 關(guān)閉文件描述符
close(sockfd);
return 0;
}
udp_client.c
#include /* See NOTES */
#include // socket() sendto()
#include
#include // inet_addr() htons()
#include // memset()
#include // fgets()
#include // close()
#include // exit()
#define LOGIN_SUCCESS 1 // 宏定義 登錄成功 1
#define LOGIN_FAILURE 0 // 宏定義 登錄失敗 0
void send_data(int sockfd,struct sockaddr_in *addr,int len){
int n = 0;
char buf[1024] = {0};
while(1)
{
printf("Input : "); // 輸入標(biāo)志
memset(buf,0,sizeof(buf)); // 重置 buf空間為 0
fgets(buf,sizeof(buf),stdin); // 獲取 輸入信息 保存到 buf中
buf[strlen(buf) - 1] = '\0'; // '\n'--->'\0'
n = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)addr,len); // 發(fā)送 buf信息 到目標(biāo)地址
if(n < 0)
{
perror("Fail to sendto");
exit(EXIT_FAILURE);
}
if(strncmp(buf,"quit",4) == 0) // 如果輸入 "quit" 退出程序
break;
}
return ;
}
void user_login(int sockfd,struct sockaddr_in *addr,struct sockaddr_in *new_addr,int len){
int n = 0;
char buf[1024] = {0};
unsigned char flag = LOGIN_FAILURE; // 登錄標(biāo)記 默認(rèn)失敗
while(1){
putchar('>'); // 輸入標(biāo)志
memset(buf,0,sizeof(buf)); // 重置 buf空間為 0
fgets(buf,sizeof(buf),stdin); // 獲取 輸入信息 保存到 buf中
buf[strlen(buf) - 1] = '\0'; // '\n'--->'\0'
n = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)addr,len); // 發(fā)送 buf信息 到目標(biāo)地址
if(n < 0){
perror("Fail to sendto");
exit(EXIT_FAILURE);
}
recvfrom(sockfd,&flag,sizeof(flag),0,(struct sockaddr *)new_addr,&len); // 從服務(wù)端 接收 登錄標(biāo)記
// 用戶登錄標(biāo)記
if(flag == LOGIN_SUCCESS) // 如果成功, 結(jié)束輸入
break;
else // 如果失敗, 繼續(xù)輸入
printf("key is invalid,try again!\n");
}
return ;
}
// ./a.out ip port
int main(int argc, const char *argv[])
{
int sockfd;
struct sockaddr_in peer_addr; // 目標(biāo)服務(wù)器 地址結(jié)構(gòu)體
struct sockaddr_in server_addr; // 子進(jìn)程服務(wù)器 地址結(jié)構(gòu)體
int len = sizeof(peer_addr);
if(argc != 3){
fprintf(stderr,"Usage : %s ip port!\n",argv[0]);
exit(EXIT_FAILURE);
}
// 1.通過socket創(chuàng)建文件描述符
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0){
perror("Fail to socket!");
return -1;
}
// 2.填充服務(wù)器的ip + port
memset(&peer_addr,0,sizeof(peer_addr)); // 重置 peer_addr空間為 0
peer_addr.sin_family = AF_INET; // 設(shè)置 協(xié)議族(IP4)
peer_addr.sin_port = htons(atoi(argv[2])); // 設(shè)置 目標(biāo)網(wǎng)絡(luò)端口
peer_addr.sin_addr.s_addr = inet_addr(argv[1]); // 設(shè)置 目標(biāo)網(wǎng)絡(luò)IP
// 3.發(fā)送登錄數(shù)據(jù)
memset(&server_addr,0,sizeof(server_addr)); // 重置 server_addr空間為 0
user_login(sockfd,&peer_addr,&server_addr,len); // 用戶登錄
// 4.發(fā)送交互數(shù)據(jù)
send_data(sockfd,&server_addr,len);
// 5.關(guān)閉文件描述符
close(sockfd);
return 0;
}