#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<ctype.h>
#include<arpa/inet.h>
// =================================================
// =================================================
// 宏定義
// =================================================
// =================================================
#define RECE_BUF_LEN 128 // receive buffer length
#define SEND_BUF_LEN 128 // send buffer length
#define PORT 0xa5a6 // port
#define SERVER_INIT_IP "192.168.50.129"
#define CLIENT_INIT_IP "192.168.50.130"
// =================================================
// =================================================
// 全局變量
// =================================================
// =================================================
int g_uDebugFlag = 0;
// 服務器端文件描述符
int server_fd;
// 發送數據的客戶端地址信息的結構體
struct sockaddr_in client_addr_from;
socklen_t socklen = sizeof(client_addr_from);
// 用於存儲接收到的數據
char receBuf[RECE_BUF_LEN] = {'0'};
// int receLen;
// 用於存儲發送的數據
char sendBuf[SEND_BUF_LEN] = {'s','e','r','v','e','r',' ',' ','s','e','n','d','!','!','!'};
struct sockaddr_in client_addr;
// sockaddr數據結構struct sockaddr_in中包含了IP地址協議類型、IP地址和端口號
struct sockaddr_in server_addr;
// =================================================
// =================================================
// 函數
// =================================================
// =================================================
// 對struct sockaddr_in進行設置
int setSockaddr_in(struct sockaddr_in *pAddr, sa_family_t sin_family, const char *strptr, uint16_t hostshort){
// 對socket數據結構內存初始化
memset(pAddr, 0, sizeof(struct sockaddr_in));
// printf("sizeof(struct sockaddr_in) = %d\n", sizeof(struct sockaddr_in));
// printf("sizeof(pAddr) = %d\n", sizeof(pAddr));
// printf("sizeof(*pAddr) = %d\n", sizeof(*pAddr));
if(g_uDebugFlag){
printf("%s at %d : hostlong = %s\n", __FUNCTION__, __LINE__, strptr);
}
(*pAddr).sin_family = sin_family;
// 兩種配置綁定IP的方式
// 方式一
inet_pton(sin_family, strptr, &(*pAddr).sin_addr.s_addr);
// 方式二
// (*pAddr).sin_port = htons(hostshort);
(*pAddr).sin_port = htons(PORT); // 這裏寫死,統一使用同一個端口號
// =============================
// 打印查看一下
// =============================
// inet_ntop調用成功時,這個指針就是inet_ntop函數的返回值
char str[INET_ADDRSTRLEN] = {0};
printf("set struct sockaddr_in IP: %s; port %d.\n", inet_ntop(AF_INET, &(*pAddr).sin_addr, str, sizeof(str)), ntohs((*pAddr).sin_port));
// printf("str = %s\n", str);
return 0;
}
int getSockaddr_in(struct sockaddr_in* pAddr){
// inet_ntop調用成功時,這個指針就是inet_ntop函數的返回值
char str[INET_ADDRSTRLEN] = {0};
printf("get struct sockaddr_in IP: %s; port %d.\n", inet_ntop(AF_INET, &(*pAddr).sin_addr, str, sizeof(str)), ntohs((*pAddr).sin_port));
// printf("str = %s\n", str);
return 0;
}
// 默認初始化服務端
int serverInit(){
// ==========================================================
// 1. 創建socket
// ==========================================================
if((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){
printf("%s at %d : server_fd created fail !!!\n", __FUNCTION__,__LINE__);
return -1;
}
// ==========================================================
// 2. 通過struct sockaddr_in 結構體設置服務器地址和監聽端口
// ==========================================================
if(setSockaddr_in(&server_addr, AF_INET, SERVER_INIT_IP, PORT) != 0){
printf("%s at %d : setSockaddr_in set fail !!!\n", __FUNCTION__,__LINE__);
return -1;
}
// ==========================================================
// 3. 使用bind()函數綁定監聽端口,將套接字文件描述符和地址類型
// 變量(struct sockaddr_in)進行綁定
// ==========================================================
if(bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
printf("%s at %d : bind fail !!!\n", __FUNCTION__,__LINE__);
return -1;
}
return 0;
}
int serverSend(char* sendBuf, int sendBufLen){
printf("=================== begin to send message =====================\n");
// ==========================================================
// 5. 向客戶端發送數據,是用sendto()函數向服務器主機發送數據
// ==========================================================
int sendLen = 0;
sendLen = sendto(server_fd, sendBuf, sendBufLen, 0, (struct sockaddr*)&client_addr, sizeof(client_addr));
// inet_ntop調用成功時,這個指針就是inet_ntop函數的返回值
char str[INET_ADDRSTRLEN] = {0};
printf("send to %s at port %d.\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)), ntohs(client_addr.sin_port));
// printf("str = %s\n", str);
printf("sendLen = %d\n", sendLen);
if(sendLen <= 0){
printf("%s at %d : sendLen 0 fail !!!\n", __FUNCTION__,__LINE__);
return -1;
}
// =========================================================
// =========================================================
// 打印發送內容
// =========================================================
// =========================================================
printf("sendBuf: %s\n", sendBuf);
printf("=================== now send message over =====================\n");
printf("\n");
// =========================================================
// =========================================================
// 發送結束之後,需要把發送緩存清空
// 如果不清空,如果再下次接收的個數少於之前的內容,
// 那麼再次發送的時候,會把上次保留的也發出來
// =========================================================
// =========================================================
memset(sendBuf, 0, sendBufLen);
// ==========================================================
// 6. 關閉套接字,是用close()函數釋放資源
// 如果在這裏關閉套接字,那麼,server_fd就被釋放了,又因爲server_fd
// 是全局變量,server_fd釋放後,就不再是socket了,recfrom就不會再
// 阻塞了,就導致了無限循環
// ==========================================================
// close(server_fd);
return 0;
}
int serverReceive(){
// ==========================================================
// 4. 接收客戶端的數據,使用recvfrom()函數接收客戶端的網絡數據
// ==========================================================
while(1){
printf("=================== begin to receive message =====================\n");
int receLen;
receLen = recvfrom(server_fd, receBuf, sizeof(receBuf), 0, (struct sockaddr*)&client_addr_from, &socklen);
printf("receLen = %d\n", receLen);
// inet_ntop調用成功時,這個指針就是inet_ntop函數的返回值
char str[INET_ADDRSTRLEN] = {0};
printf("receive from %s at port %d.\n", inet_ntop(AF_INET, &client_addr_from.sin_addr, str, sizeof(str)), ntohs(client_addr_from.sin_port));
// printf("str = %s\n", str);
if(receLen <= 0){
printf("%s at %d : receLen 0 fail !!!\n", __FUNCTION__,__LINE__);
continue;
}
// =========================================================
// =========================================================
// 打印接收內容
// =========================================================
// =========================================================
printf("receBuf = %s\n", receBuf);
printf("=================== now receive message over =====================\n");
printf("\n");
if(setSockaddr_in(&client_addr, AF_INET, inet_ntop(AF_INET, &client_addr_from.sin_addr, str, sizeof(str)), ntohs(client_addr_from.sin_port)) != 0){
printf("%s at %d : setSockaddr_in set fail !!!\n", __FUNCTION__,__LINE__);
continue;
}
// ===================================================
// ===================================================
// 注意:如果不serverSend(receBuf,需要在這裏
// 把接收buf清空,不然輸出信息有誤。
memset(receBuf, 0, receLen);
// ===================================================
// ===================================================
// if(serverSend(receBuf, receLen) != 0){
// printf("%s at %d : serverSend fail !!!\n", __FUNCTION__,__LINE__);
// continue;
// }
}
// ==========================================================
// 5. 關閉套接字,是用close()函數釋放資源
// ==========================================================
close(server_fd);
return 0;
}
int main(){
if(serverInit() != 0){
printf("%s at %d : serverInit fail !!!\n", __FUNCTION__,__LINE__);
return -1;
}
if(getSockaddr_in(&server_addr) != 0){
printf("%s at %d : getSockaddr_in server_addr fail !!!\n", __FUNCTION__,__LINE__);
return -1;
}
if(serverReceive() != 0){
printf("%s at %d : serverReceive fail !!!\n", __FUNCTION__,__LINE__);
return -1;
}
}