? ? ? ? 2023.6.28 正式開始學(xué)習(xí)網(wǎng)絡(luò)編程。 每一章每一節(jié)的筆記都會(huì)記錄在博客中以便復(fù)習(xí)。
第1章
1.1理解網(wǎng)絡(luò)編程和套接字
? ? ? ? 網(wǎng)絡(luò)編程又叫套接字編程。所謂網(wǎng)絡(luò)編程,就是編寫程序使兩臺(tái)連網(wǎng)的計(jì)算機(jī)相互交換數(shù)據(jù)。 為什么叫套接字編程? 我們平常將插頭插入插座上就能從電網(wǎng)中獲取電力,同樣的道理,為了與遠(yuǎn)程計(jì)算機(jī)進(jìn)行數(shù)據(jù)傳輸,需要連接到因特網(wǎng),而編程中的“套接字”就是用來鏈接網(wǎng)絡(luò)的工具。
? ? ? ? 服務(wù)器端創(chuàng)建的套接字又叫服務(wù)器端套接字或者監(jiān)聽套接字。 其請(qǐng)求連接的套接字創(chuàng)建過程分為四步:
- 調(diào)用socket函數(shù)創(chuàng)建套接字
- 調(diào)用bind函數(shù)分配IP地址和端口號(hào)
- 調(diào)用listen函數(shù)轉(zhuǎn)為可接受請(qǐng)求狀態(tài)
- 調(diào)用accept函數(shù)受理連接請(qǐng)求
? ? ? ? 請(qǐng)求連接的客戶端套接字創(chuàng)建過程如下兩步:
- 調(diào)用socket函數(shù)創(chuàng)建套接字
- 調(diào)用connect函數(shù)向服務(wù)器端發(fā)送連接請(qǐng)求
????????值得注意的是:創(chuàng)建完套接字,并不會(huì)馬上區(qū)分為服務(wù)器端或客戶端。 如果接下來緊接著調(diào)用bind、listen等函數(shù)則成為服務(wù)器端套接字;如果調(diào)用connect函數(shù)則稱為客戶端套接字。
? ? ? ? 接下來在linux環(huán)境中編譯并執(zhí)行上述兩個(gè)實(shí)例:hello_server.c文件和hello_client.c文件。
????????分別對(duì)客戶端和服務(wù)端程序進(jìn)行編譯:????
gcc hello_server.c -o hserver
gcc hello_client.c -o hclient
????????該命令中的-o是用來指定可執(zhí)行文件名的可選參數(shù),因此,編譯后將生成可執(zhí)行文件hserver和hclient。
????????????運(yùn)行:
./hserver 9190
./hclient 127.0.0.1 9190
????????運(yùn)行的時(shí)候,首先在?9190 端口啟動(dòng)服務(wù),然后 heserver 就會(huì)一直等待客戶端進(jìn)行響應(yīng),當(dāng)客戶端監(jiān)聽位于本地的 IP 為 127.0.0.1 的地址的9190端口時(shí),客戶端就會(huì)收到服務(wù)端的回應(yīng),輸出`Hello World!`。
? ? ? ? ps:執(zhí)行過程中輸入的127.0.0.1是本地計(jì)算機(jī)的IP地址。 如果在同一臺(tái)計(jì)算機(jī)中同時(shí)運(yùn)行服務(wù)器端和客戶端,將采用這種連接方式。 但如果服務(wù)器端和客戶端在不同計(jì)算機(jī)中運(yùn)行,則應(yīng)采用服務(wù)器端所在計(jì)算機(jī)的IP地址。
1.2基于Linux的文件操作
??????????????????????????????????????????????????底層訪問和文件描述符
? ? ? ? 在linux中,socket也是文件的一種,因此在網(wǎng)絡(luò)數(shù)據(jù)傳輸過程中可以使用文件I/O相關(guān)的函數(shù)。而Windows需要區(qū)分socket和文件,因此在Windows中需要調(diào)用特殊的數(shù)據(jù)傳輸相關(guān)函數(shù)。
? ? ? ??每當(dāng)生成文件或套接字,操作系統(tǒng)將返回分配給他們的整數(shù),(即文件描述符),這個(gè)整數(shù)將成為程序員與操作系統(tǒng)之間良好溝通的渠道,實(shí)際上,文件描述符是為了方便稱呼系統(tǒng)創(chuàng)建的文件或套接字而賦予的數(shù)。 文件描述符也成為文件句柄,“句柄”是Windows中的術(shù)語,Linux平臺(tái)則使用“描述符”。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 打開文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int flag);
/*
成功時(shí)返回文件描述符,失敗時(shí)返回-1
path : 文件名的字符串地址
flag : 文件打開模式信息
*/
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?關(guān)閉文件
#include <unistd.h>
int close(int fd);
/*
成功時(shí)返回 0 ,失敗時(shí)返回 -1
fd : 需要關(guān)閉的文件或套接字的文件描述符
*/
????????若調(diào)用此函數(shù)同時(shí)傳遞文件描述符參數(shù),則關(guān)閉(終止)響應(yīng)文件。另外需要注意的是,此函數(shù)不僅可以關(guān)閉文件,還可以關(guān)閉套接字。再次證明了「Linux 操作系統(tǒng)不區(qū)分文件與套接字」的特點(diǎn)。?
????????????????????????????????????????????????? ? 將數(shù)據(jù)寫入文件
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
/*
成功時(shí)返回寫入的字節(jié)數(shù) ,失敗時(shí)返回 -1
fd : 顯示數(shù)據(jù)傳輸對(duì)象的文件描述符
buf : 保存要傳輸數(shù)據(jù)的緩沖值地址
nbytes : 要傳輸數(shù)據(jù)的字節(jié)數(shù)
*/
????????在此函數(shù)的定義中,size_t 是通過 typedef 聲明的 unsigned int 類型。對(duì) ssize_t 來說,ssize_t 前面多加的 s 代表 signed ,即 ssize_t 是通過 typedef 聲明的 signed int 類型。
創(chuàng)建新文件并保存數(shù)據(jù):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
void error_handling(char *message);
int main()
{
int fd;
char buf[] = "Let's go!\n";
// O_CREAT | O_WRONLY | O_TRUNC 是文件打開模式,將創(chuàng)建新文件,并且只能寫。如存在 data.txt 文件,則清空文件中的全部數(shù)據(jù)。
fd = open("data.txt", O_CREAT | O_WRONLY | O_TRUNC);
if (fd == -1)
error_handling("open() error!");
printf("file descriptor: %d \n", fd);
// 向?qū)?yīng) fd 中保存的文件描述符的文件傳輸 buf 中保存的數(shù)據(jù)。
if (write(fd, buf, sizeof(buf)) == -1)
error_handling("write() error!");
close(fd);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
? ? ? ? ?運(yùn)行后會(huì)生成一個(gè)data.txt
的文件,里面有Let's go!
??????????????????????????????????????????????????????? 讀取文件中的數(shù)據(jù)
????????與之前的write()
函數(shù)相對(duì)應(yīng),read()函數(shù)
用來輸入(接收)數(shù)據(jù):
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
/*
成功時(shí)返回接收的字節(jié)數(shù)(但遇到文件結(jié)尾則返回 0),失敗時(shí)返回 -1
fd : 顯示數(shù)據(jù)接收對(duì)象的文件描述符
buf : 要保存接收的數(shù)據(jù)的緩沖地址值。
nbytes : 要接收數(shù)據(jù)的最大字節(jié)數(shù)
*/
????????下面代碼通過 read() 函數(shù)讀取 data.txt 中保存的數(shù)據(jù):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define BUF_SIZE 100
void error_handling(char *message);
int main()
{
int fd;
char buf[BUF_SIZE];
fd = open("data.txt", O_RDONLY);
if (fd == -1)
error_handling("open() error!");
printf("file descriptor: %d \n", fd);
if (read(fd, buf, sizeof(buf)) == -1)
error_handling("read() error!");
printf("file data: %s", buf);
close(fd);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
實(shí)驗(yàn)結(jié)果:?
?????????low.open.c創(chuàng)建文件并保存文件數(shù)據(jù),運(yùn)行之后返回文件描述符3。用cat命令輸出data.txt的文件內(nèi)容,可以確認(rèn)確實(shí)向文件傳輸了數(shù)據(jù)。
? ? ? ? low.read.c程序則通過read函數(shù)讀取了data.txt中保存的數(shù)據(jù)。運(yùn)行之后打印文件描述符以及文件的內(nèi)容。?
? ? ? ? fd_seri.c 程序同時(shí)創(chuàng)建文件和套接字:
????fd1 = socket(PF_INET, SOCK_STREAM, 0);
? ? fd2 = open("test.dat", O_CREAT | O_WRONLY | O_TRUNC);
? ? fd3 = socket(PF_INET, SOCK_DGRAM, 0);
然后分別打印其文件描述符得到3、4、5,描述符從3開始以由小到大的順序編號(hào),因?yàn)?、1、2是分配給標(biāo)注能I/O的描述符。如下圖:
???????
1.5 習(xí)題
1.套接字在網(wǎng)絡(luò)編程中的作用是什么?為何稱它為套接字?
????????套接字是一種用于在網(wǎng)絡(luò)上進(jìn)行通信的編程接口。套接字允許不同計(jì)算機(jī)上的進(jìn)程通過網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)傳輸。它提供了一種機(jī)制,使得計(jì)算機(jī)之間可以建立連接、發(fā)送和接收數(shù)據(jù)。套接字使得應(yīng)用程序能夠利用網(wǎng)絡(luò)進(jìn)行通信,實(shí)現(xiàn)客戶端和服務(wù)器之間的數(shù)據(jù)交換。
????????它被稱為套接字,是因?yàn)樗惐扔陔娫捦ㄐ胖械牟遄鳛榫W(wǎng)絡(luò)通信的端點(diǎn)連接點(diǎn)。
2.在服務(wù)器端創(chuàng)建套接字后,會(huì)依次調(diào)用listen函數(shù) 和accept函數(shù)。請(qǐng)比較并說明二者作用。
????????listen函數(shù)用于將套接字設(shè)置為被動(dòng)監(jiān)聽模式,以接受客戶端的連接請(qǐng)求。
????????accept函數(shù)用于接受客戶端的連接請(qǐng)求,并創(chuàng)建一個(gè)新的套接字來處理與該客戶端的通信。
3.Linux中,對(duì)套接字?jǐn)?shù)據(jù)進(jìn)行I/O時(shí)可以直接使用文件I/O相關(guān)函數(shù);而在Windows中則不可以,原因?yàn)楹危?/h4>
????????這是因?yàn)樵赨nix-like系統(tǒng)中,包括Linux,一切皆文件的思想被廣泛采用,將各種資源(包括套接字)都抽象為文件描述符的形式,統(tǒng)一了數(shù)據(jù)的讀寫接口。
????????然而,在Windows操作系統(tǒng)中,套接字不被視為文件描述符,因此不能直接使用文件I/O相關(guān)函數(shù)進(jìn)行數(shù)據(jù)的讀寫操作。
4.創(chuàng)建套接字后一般會(huì)給它分配地址, 為什么?為了完成地址分配需要調(diào)用哪個(gè)函數(shù)?
????????在網(wǎng)絡(luò)編程中,創(chuàng)建套接字后需要給它分配地址,主要是為了使其他計(jì)算機(jī)能夠找到并與該套接字建立連接。通過分配地址,可以指定套接字的IP地址和端口號(hào),從而唯一標(biāo)識(shí)該套接字在網(wǎng)絡(luò)中的位置。文章來源:http://www.zghlxwxcb.cn/news/detail-507845.html
????????為了完成地址分配,需要調(diào)用bind函數(shù)。bind函數(shù)用于將套接字與特定的IP地址和端口號(hào)進(jìn)行綁定。文章來源地址http://www.zghlxwxcb.cn/news/detail-507845.html
到了這里,關(guān)于《TCP IP網(wǎng)絡(luò)編程》的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!