目錄題目一解析代碼實現題目二:解析代碼實現tcp_client.ctcp_server.c 題目一 設計程式實現解析www.baidu.com的功能變數名稱,把獲取到的百度的IP地址全部輸出到終端並驗證是否正確。 解析 主機打算響應某個網站的網路請求,但是只知道網站功能變數名稱是無法通信的,需要對功能變數名稱進行地址解析, ...
目錄
題目一
設計程式實現解析www.baidu.com的功能變數名稱,把獲取到的百度的IP地址全部輸出到終端並驗證是否正確。
解析
主機打算響應某個網站的網路請求,但是只知道網站功能變數名稱是無法通信的,需要對功能變數名稱進行地址解析,得到網站的公有IP地址,所以Linux系統提供了名稱叫做gethostbyname()的函數介面實現該功能,使用規則如下:
代碼實現
/********************************************************************************
*
* file name: demo_domain.c
* author : [email protected]
* date : 2024/06/04
* function :
* 設計程式實現解析www.baidu.com的功能變數名稱,
* 把獲取到的百度的IP地址全部輸出到終端並驗證是否正確。
* note :
* 1.可以通過ping 百度功能變數名稱,觀察返回值的IP地址來驗證獲取值是否正確
* 2.通過命令行輸入不同的功能變數名稱,來獲取不同功能變數名稱的IP地址
* 例如:./xxx xxx(有效功能變數名稱地址)
* version :
*
* CopyRight (c) 2023-2024 [email protected] All Right Reseverd
*
* ******************************************************************************/
/****************************頭文件*********************************************/
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//對參數進行錯誤處理
if(argc != 2)
{
fprintf(stderr, "argc error, errno: %d, %s\n", errno, strerror(errno));
exit(1);
}
//利用gethostbyname函數介面,獲取目標功能變數名稱所包含的信息
struct hostent *hostent = gethostbyname(argv[1]);
//定義一個結構體變數,用於存儲IP地址
struct in_addr ip_hostent;
//定義一個迴圈數組下標
int i = 0;
//迴圈將獲取得到的hostent結構體中的ip地址轉換為點分十進位字元串輸出
while(hostent->h_addr_list[i] != NULL)
{
//將獲取IP地址進行類型轉換,存儲在ip_hostent中
ip_hostent.s_addr = *(uint32_t *)(hostent->h_addr_list[i]);
//利用函數介面inet_ntoa(),將網路節序類型轉換為點分十進位字元串類型並輸出
printf("%s ip :%s\n", argv[1], inet_ntoa(ip_hostent));
//對數組下標進行偏移
i++;
}
return 0;
}
題目二:
利用TCP協議,設計兩個程式,一個作為客戶端,先發送連接申請給服務端,發送消息給服務端;另一個作為服務端,接受客戶端申請,並接受客戶端發送的消息。
解析
- listen函數不具有阻塞特性,因為listen只是相當於把socket的屬性更改為被動連接,可以接收其他進程的連接,設置好後便會返回,監聽的實質有操作系統完成。
- 調用listen函數需要設置監聽隊列的最大容量,該監聽隊列容量不是指的是服務端能連接客戶端的數量。
- accept函數具有阻塞特性,調用一次accept只能接收一個客戶端申請請求,並且會生成返回一個新的、專屬於當前連接的客戶端的文件描述符,該描述符一定要存儲起來,方便服務端後續與客戶端點對點通信。
代碼實現
tcp_client.c
/********************************************************************************
*
* file name: tcp_client.c
* author : [email protected]
* date : 2024/06/05
* function : 該案例是掌握TCP協議通信方式,該代碼文件將實現客戶端發送信息給服務端的功能。
* note :
* 1. 需要註意的是connect是非阻塞特性,所以一定要接收返回值進行判斷,
* 否則會出現沒有連接成功,卻進入發送的錯誤狀態。
* 2. 由於該函數部分參數由命令行傳入,所以在執行文件時要加上參數
* 例如:./xxx xxx(埠號) xxx.xxx.xxx.xxx(服務端IP地址--點分十進位)
* version :
*
* CopyRight (c) 2023-2024 [email protected] All Right Reseverd
*
* ******************************************************************************/
/****************************頭文件*********************************************/
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc,const char *argv[])
{
//判斷命令行參數
if(argc != 3)
{
fprintf(stderr, "argc error,errno:%d,%s\n",errno,strerror(errno));
exit(1);
}
//創建TCP的套接字文件
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
if(tcp_socket == -1)
{
fprintf(stderr, "tcp socket error,errno:%d,%s\n",errno,strerror(errno));
exit(2);
}
//綁定本地信息--如果只打算實習發送功能,該步驟也可以省略
struct sockaddr_in host_addr;
host_addr.sin_family = AF_INET; //協議族,為固定巨集
host_addr.sin_port = htons(atoi(argv[1])); //想要接收信息進程的埠號
host_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址,填寫該巨集定義代表任意網口都可以接收值
bind(tcp_socket,(struct sockaddr *)&host_addr, sizeof(host_addr));
//向目標主機發送消息,需要設置目標埠和目標地址
char buf[128] = {0};
struct sockaddr_in dest_addr;
socklen_t flag_dest = sizeof(dest_addr);
dest_addr.sin_family = AF_INET; //協議族,是固定的
dest_addr.sin_port = htons(atoi(argv[1])); //伺服器運行進程的埠號,必須轉換為網路位元組序
dest_addr.sin_addr.s_addr = inet_addr(argv[2]); //要發送對象的IP地址,必須轉換為點分十進位字元串形式
//使用connect函數介面向伺服器發起連接申請
int flag_connect = connect(tcp_socket, (struct sockaddr *)&dest_addr, flag_dest);
if(flag_connect == -1)
{
fprintf(stderr, "connect socket error,errno:%d,%s\n",errno,strerror(errno));
exit(3);
}
//迴圈向伺服器發送消息
while(1)
{
//從鍵盤獲取信息
scanf("%s", buf);
write(tcp_socket, buf, strlen(buf));
bzero(buf, sizeof(buf));
}
return 0;
}
tcp_server.c
/********************************************************************************
*
* file name: tcp_server.c
* author : [email protected]
* date : 2024/06/17
* function : 該案例是掌握進行模塊化編程思想,以及封裝函數介面流程
* note :
* 1. 需要註意的是listen是非阻塞特性,accept才具有阻塞特性
* 2. accpet在成功連接客戶端後,會重新創建一個新的、專屬於連接客戶端的
* 套接字文件描述符,方便服務端與客戶端點對點通信
* 3. 由於該函數部分參數由命令行傳入,所以在執行文件時要加上參數
* 例如:./xxx xxx(埠號)
* version :
*
* CopyRight (c) 2023-2024 [email protected] All Right Reseverd
*
* ******************************************************************************/
/****************************頭文件*********************************************/
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//對命令行傳入參數數量進行判斷
if(argc != 2)
{
fprintf(stderr, "argc error,errno:%d,%s\n",errno,strerror(errno));
exit(1);
}
//1.創建TCP套接字
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_socket == -1)
{
fprintf(stderr, "tcp socket error,errno:%d,%s\n",errno,strerror(errno));
exit(2);
}
//2.綁定自身的IP地址和埠,由於該處伺服器功能是接受信息,所以一定要綁定自身信息
struct sockaddr_in host_addr;
host_addr.sin_family = AF_INET; //協議族,是固定的
host_addr.sin_port = htons(atoi(argv[1])); //目標埠,必須轉換為網路位元組序
host_addr.sin_addr.s_addr = htonl(INADDR_ANY); //目標地址 INADDR_ANY 這個巨集是一個整數,所以需要使用htonl轉換為網路位元組序
bind(tcp_socket,(struct sockaddr *)&host_addr, sizeof(host_addr));
//3.設置監聽,且監聽隊列最大容量設置為5
listen(tcp_socket,5);
//4.等待接受客戶端的連接請求
struct sockaddr_in client;
socklen_t client_len = sizeof(client);
//調用accept函數介面後,一定要接收其返回值,因為該返回值為accept創建的新的、專屬於連接客戶端的套接字文件描述符
int connect_fd = accept(tcp_socket,(struct sockaddr *)&client,&client_len); //會阻塞
char buf[128] = {0};
//5.說明雙方建立連接,此時可以接收數據
while(1)
{
read(connect_fd,buf,sizeof(buf));
printf("recv from [%s],data is = %s\n", inet_ntoa(client.sin_addr) ,buf);
bzero(buf,sizeof(buf));
}
return 0;
}