LINUX之网络编程j简述
一、TCP、IP概述
?
1、tcp/ip 模型 4 層
- 應用層{ http 超文本傳輸協(xié)議 ?ftp 文件傳輸協(xié)議 ?telnet 遠程登錄 ?ssh 安全外殼協(xié)議 ?stmp 簡單郵件發(fā)送、pop3 收郵件}
- 傳輸層{ tcp 傳輸控制協(xié)議, ?udp 用戶數(shù)據(jù)包協(xié)議}
- 網(wǎng)絡層{ip 網(wǎng)際互聯(lián)協(xié)議 icmp 網(wǎng)絡控制消息協(xié)議 igmp 網(wǎng)絡組管理協(xié)議}
- 網(wǎng)絡接口層{arp 地址轉換協(xié)議,rarp 反向地址轉換協(xié)議, mpls 多協(xié)議標簽交換}
- TCP 協(xié)議: 傳輸控制協(xié)議 面向連接的協(xié)議 能保證傳輸安全可靠 速度慢(有 3 次握手)
- UDP 協(xié)議: 用戶數(shù)據(jù)包協(xié)議 非面向連接 速度快 不可靠
- 通常是 ip 地址后面跟上端口號: ip 用來定位主機 port 區(qū)別應用(進程) ?http 的端口號 80 ssh-->22 telnet-->23 ftp-->21 用戶自己定義的通常要大于 1024
?
2、 TCP/IP 協(xié)議族的每一層的作用
?
- 網(wǎng)絡接口層: 負責將二進制流轉換為數(shù)據(jù)幀, 并進行數(shù)據(jù)幀的發(fā)送和接收。要注意的是數(shù)據(jù)幀是獨立的網(wǎng)絡信息傳輸單元。
- 網(wǎng)絡層: 負責將數(shù)據(jù)幀封裝成 IP 數(shù)據(jù)報, 并運行必要的路由算法。
- 傳輸層: 負責端對端之間的通信會話連接和建立。 傳輸協(xié)議的選擇根據(jù)數(shù)據(jù)傳輸方式而定。
- 應用層: 負責應用程序的網(wǎng)絡訪問, 這里通過端口號來識別各個不同的進程。
?
3、 TCP協(xié)議
?????TCP 是 TCP/IP 體系中面向連接的運輸層協(xié)議, 它提供全雙工和可靠交付的服務。 它采用許多機制來確保端到端結點之間的可靠數(shù)據(jù)傳輸, 如采用序列號、 確認重傳、 滑動窗口等。
?
- 三次握手協(xié)議
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
?
4、協(xié)議的選擇
?
?????對數(shù)據(jù)要求高可靠性的應用需選擇 TCP 協(xié)議, 如驗證、 密碼字段的傳送都是不允許出錯的, 而對數(shù)據(jù)的可靠性要求不那么高的應用可選擇 UDP 傳送。
?????TCP 協(xié)議在傳送過程中要使用三次握手、重傳確認等手段來保證數(shù)據(jù)傳輸?shù)目煽啃浴?使用 TCP 協(xié)議會有較大的時延, 因此不適合對實時性要求較高的應用, 如 VOIP、 視頻監(jiān)控等。 相反, UDP 協(xié)議則在這些應用中能發(fā)揮很好的作用。
?
二、網(wǎng)絡的相關概念
?
1、套接字
?
? ? ?網(wǎng)絡通信,歸根到底還是進程間的通信(不同計算機上的進程間通信)。網(wǎng)絡地址,即IP 地址只能確定進程所在的計算機,而一臺計算機上很可能同時運行著多個進程,所以僅憑網(wǎng)絡地址還不能確定到底是和網(wǎng)絡中的哪一個進程進行通信, 因此套接口中還需要包括其他的信息, 也就是端口號(PORT) 。一個端口號一次只能分配給一個進程,使用端口號和網(wǎng)絡地址的組合可以唯一的確定整個網(wǎng)絡中的一個網(wǎng)絡進程。
?
? ? ?例如, 如網(wǎng)絡中某一臺計算機的 IP 為 10.92.20.160, 操作系統(tǒng)分配給計算機中某一應用程序進程的端口號為 1500, 則此時 10.92.20.160 1500 就構成了一個套接口。
?
? ? ?端口號的范圍從 0~65535。一種是常用的應用程序固定使用的“周知的端口”,其值一般為0~1023。http 的端口號是 80, ftp 為 21, ssh 為 22, telnet 為 23 等。 還有一類是用戶自己定義的, 通常是大于 1024 的整型值。
ipv4 地址: 32bit, 4 字節(jié), 通常采用點分十進制記法(如128.11.3.31),但是在網(wǎng)絡中,socket 編程中使用的則是二進制值, 這就需要將這兩個數(shù)值進行轉換。
?
2、ip地址分類
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
?
3、socket 類型
?
(1) 流式 socket(SOCK_STREAM) ?用于 TCP 通信
?????流式套接字提供可靠的、 面向連接的通信流; 它使用 TCP 協(xié)議, 從而保證了數(shù)據(jù)傳輸?shù)恼_性和順序性。
?
(2) 數(shù)據(jù)報 socket(SOCK_DGRAM) ?用于 UDP 通信
?????數(shù)據(jù)報套接字定義了一種無連接的服務, 數(shù)據(jù)通過相互獨立的報文進行傳輸, 是無序的, 并且不保證是可靠、 無差錯的。 它使用數(shù)據(jù)報協(xié)議 UDP。
?
(3) 原始 socket(SOCK_RAW) ?用于新的網(wǎng)絡協(xié)議實現(xiàn)的測試等
原始套接字允許對底層協(xié)議如 IP 或 ICMP 進行直接訪問, 它功能強大但使用較為不便, 主要用于一些協(xié)議的開發(fā)。
?
4、socket信息數(shù)據(jù)結構
?
struct sockaddr ? ? ? ? ---->網(wǎng)絡傳輸常用
{
?????unsigned short sa_family; ? ? ?????/*地址族*/
?????char sa_data[14]; ? ? ? ? ? ? /*14 字節(jié)的協(xié)議地址, 包含該 socket 的 IP 地址和端口號。 */
};
?
struct sockaddr_in ? ?? ---->編程常用
{
?????short int sa_family; ? ? ? ?? ?????/*地址族*/
?????unsigned short int sin_port; ? ? ? /*端口號*/
?????struct in_addr sin_addr; ? ? ? ? ??/*IP 地址*/
?????unsigned char sin_zero[8]; ? ? ? ?
?????/*填充 0 以保持與 struct sockaddr 同樣大小*/
};
?
struct in_addr
{
?????unsigned long int
?????s_addr;
?????/* 32 位 IPv4 地址, 網(wǎng)絡字節(jié)序 */
};
?
頭文件<netinet/in.h>
sa_family: AF_INET --->IPv4 協(xié)議 ??AF_INET6 --->?IPv6 協(xié)議
?
5、數(shù)據(jù)存儲優(yōu)先順序的轉換
?
? ? ?計算機數(shù)據(jù)存儲有兩種字節(jié)優(yōu)先順序: 高位字節(jié)優(yōu)先(稱為大端模式) 和低位字節(jié)優(yōu)先(稱為小端模式)。
低地址存儲數(shù)據(jù)的低字節(jié), 高地址存儲數(shù)據(jù)的高字節(jié) --->?小端模式
高地址存儲數(shù)據(jù)的低字節(jié), 低地址存儲數(shù)據(jù)高字節(jié) ?? ---> ?大端模式
而端口號和 IP 地址都是以網(wǎng)絡字節(jié)序存儲的, 不是主機字節(jié)序, 網(wǎng)絡字節(jié)序都是大端模式。故需要進行相互轉化。
?
四個函數(shù):
htons(),ntohs(),htonl()和 ntohl().這四個地址分別實現(xiàn)網(wǎng)絡字節(jié)序和主機字節(jié)序的轉化, 這里的 h代表 host,n 代表 network, s 代表 short, l 代表 long。 通常 16 位的 IP 端口號用 s 代表。
?
6、地址格式轉化
?
? ? ?用戶在表達網(wǎng)絡地址時采用的是點分十進制表示的數(shù)值,socket 編程中使用的則是 32 位的網(wǎng)絡字節(jié)序的二進制值, 這就需要將這兩個數(shù)值進行轉換。Ipv4 中用到的函數(shù)有 inet_aton()、 inet_addr()和 inet_ntoa(),而 IPV4 和 Ipv6 兼容的函數(shù)有 inet_pton()和 inet_ntop()。
?
IPv4 的函數(shù)原型:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
?????int inet_aton(const char *straddr, struct in_addr
?????*addrptr);
?????char *inet_ntoa(struct in_addr inaddr);
?????in_addr_t inet_addr(const char *straddr);
?????// in_addr_t 就是 unsigned long int ,代
表 s_addr函數(shù) inet_aton(): 將點分十進制數(shù)的 IP 地址轉換成為網(wǎng)絡字節(jié)序的 32 位二進制數(shù)值。 返回值:成功,則返回 1, 不成功返回 0.
?????參數(shù) straddr: 存放輸入的點分十進制數(shù) IP 地址字符串。
?????參數(shù) addrptr: 傳出參數(shù), 保存網(wǎng)絡字節(jié)序的 32 位二進制數(shù)值。
?????函數(shù) inet_ntoa(): 將網(wǎng)絡字節(jié)序的 32 位二進制數(shù)值轉換為點分十進制的 IP 地址。
?????函數(shù) inet_addr(): 功能與 inet_aton 相同, 但是結果傳遞的方式不同。 inet_addr()若成功則返回32 位二進制的網(wǎng)絡字節(jié)序地址。
?
IPv4 和 IPv6 的函數(shù)原型:
#include <arpa/inet.h>
?????int inet_pton(int family, const char *src, void *dst);
?????const char *inet_ntop(int family, const void *src, char *dst, socklen_t len);
?
7、地址名字地址轉化
?
函數(shù)原型:
?
#include <netdb.h>
struct hostent* gethostbyname(const char* hostname);
struct hostent* gethostbyaddr(const char* addr, size_t len,
int family);
?
結構體:
struct hostent
{
?????char *h_name; ? ? ?????/*正式主機名*/
?????char **h_aliases; ? ? ?/*主機別名*/
?????int h_addrtype; ? ? ?? /*主機 IP 地址類型 IPv4 為 AF_INET*/
?????int h_length; ? ? ?????/*主機 IP 地址字節(jié)長度, 對于 IPv4 是 4 字節(jié), 即 32 位*/
?????char **h_addr_list; ?????/*主機的 IP 地址列表*/
}
?
8、網(wǎng)絡編程函數(shù)---匯總
?
1. 頭文件包含:
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include <stdio.h>
#include <stdlib.h>
? ?
TCP服務器端:
2. socket 函數(shù): 生成一個套接口描述符。
?
原型: int socket(int domain,int type,int protocol);
參數(shù): domain?{ AF_INET: Ipv4 網(wǎng)絡協(xié)議 AF_INET6: IPv6 網(wǎng)絡協(xié)議} ?type{tcp: SOCK_STREAM udp: SOCK_DGRAM}
protocol指定 socket 所使用的傳輸協(xié)議編號。 通常為 0.
返回值: 成功則返回套接口描述符, 失敗返回-1。
?
常用實例: int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1){perror("socket");exit(-1);}
?
3. bind 函數(shù): 用來綁定一個端口號和 IP 地址, 使套接口與指定的端口號和 IP 地址相關聯(lián)。
?
原型: int bind(int sockfd,struct sockaddr * my_addr,int addrlen);
參數(shù): sockfd?為前面 socket 的返回值。
my_addr?為結構體指針變量
?
4. listen 函數(shù): 使服務器的這個端口和 IP 處于監(jiān)聽狀態(tài), 等待網(wǎng)絡中某一客戶機的連接請求。 如果客戶
端有連接請求, 端口就會接受這個連接。
?
原型: int listen(int sockfd,int backlog);
參數(shù): sockfd?為前面 socket 的返回值.即 sfd
backlog?指定同時能處理的最大連接要求, 通常為 10 或者 5。 最大值可設至 128
返回值: 成功則返回 0, 失敗返回-1
?
常用實例: if(listen(sfd, 10) == -1)
{perror("listen");close(sfd);exit(-1);}
?
5. accept 函數(shù): 接受遠程計算機的連接請求, 建立起與客戶機之間的通信連接。 服務器處于監(jiān)聽狀態(tài)時,如果某時刻獲得客戶機的連接請求, 此時并不是立即處理這個請求,而是將這個請求放在等待隊列中,當系統(tǒng)空閑時再處理客戶機的連接請求。當 accept 函數(shù)接受一個連接時, 會返回一個新的 socket 標識符,以后的數(shù)據(jù)傳輸和讀取就要通過這個新的 socket 編號來處理,原來參數(shù)中的 socket 也可以繼續(xù)使用, 繼
服務器跟一個客戶端連接成功, 會有兩個套接字。)續(xù)監(jiān)聽其它客戶機的連接請求。
?
原型: int accept(int s,struct sockaddr * addr,int * addrlen);
參數(shù): s?為前面 socket 的返回值.即 sfd。addr為結構體指針變量,和 bind 的結構體是同種類型的, 系統(tǒng)會把遠程主機的信息(遠程主機的地址和端口號信息) 保存到這個指針所指的結構體中。addrlen?表示結構體的長度,為整型指針。
返回值: 成功則返回新的 socket 處理代碼 new_fd, 失敗返回-1
?
常用實例:
struct sockaddr_in clientaddr;
memset(&clientaddr, 0, sizeof(struct sockaddr));
int addrlen = sizeof(struct sockaddr);
int new_fd = accept(sfd, (struct sockaddr*)&clientaddr, &addrlen);
if(new_fd == -1)
?????{
? ?? ?????perror("accept");close(sfd);exit(-1);
?????}
?????printf("%s %d success connect\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
?
6. recv 函數(shù): 用新的套接字來接收遠端主機傳來的數(shù)據(jù), 并把數(shù)據(jù)存到由參數(shù) buf 指向的內存空間
原型: int recv(int sockfd,void *buf,int len,unsigned int flags);
?
參數(shù):sockfd為前面 accept 的返回值.即 new_fd, 也就是新的套接字。
buf表示緩沖區(qū)
len表示緩沖區(qū)的長度
flags通常為 0
返回值: 成功則返回實際接收到的字符數(shù), 可能會少于你所指定的接收長度。 失敗返回-1。
?
常用實例:
char buf[512] = {0};
if(recv(new_fd, buf, sizeof(buf), 0) == -1)
{
?????perror("recv");close(new_fd);close(sfd);exit(-1);
}
puts(buf);
?
7. send 函數(shù): 用新的套接字發(fā)送數(shù)據(jù)給指定的遠端主機
原型: int send(int s,const void * msg,int len,unsigned int flags);
參數(shù): s為前面 accept 的返回值.即 new_fd
msg一般為常量字符串 ? ??len表示長度 ? ? ?flags通常為 0
返回值: 成功則返回實際傳送出去的字符數(shù), 可能會少于你所指定的發(fā)送長度。 失敗返回-1
?
常用實例: if(send(new_fd, "hello", 6, 0) == -1)
{perror("send");close(new_fd);close(sfd);exit(-1);}
?
8. close 函數(shù): 當使用完文件后若已不再需要則可使用 close()關閉該文件, 并且 close()會讓數(shù)據(jù)寫回磁盤,并釋放該文件所占用的資源
?
原型: int close(int fd);
參數(shù): fd?為前面的 sfd,new_fd
返回值: 若文件順利關閉則返回 0, 發(fā)生錯誤時返回-1
?
常用實例: close(new_fd);
close(sfd);
?
TCP客戶端:
1. connect 函數(shù): 用來請求連接遠程服務器, 將參數(shù) sockfd 的 socket 連至參數(shù) serv_addr 指定的服務器
IP 和端口號上去。
?
原型: int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
參數(shù): sockfd?為前面 socket 的返回值, 即 sfd
serv_addr?為結構體指針變量, 存儲著遠程服務器的 IP 與端口號信息。
addrlen?表示結構體變量的長度
返回值: 成功則返回 0, 失敗返回-1
?
常用實例:
struct sockaddr_in seraddr;//請求連接服務器
memset(&seraddr, 0, sizeof(struct sockaddr));
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888); //服務器的端口號
seraddr.sin_addr.s_addr = inet_addr("192.168.0.101"); //服務器的 ip
if(connect(sfd, (struct sockaddr*)&seraddr, sizeof(struct sockaddr)) == -1)
{
?????perror("connect");
?????close(sfd);
?????exit(-1);
}
?
UDP:
1、sendto()函數(shù)原型:
?
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
?
該函數(shù)比 send()函數(shù)多了兩個參數(shù), to 表示目地機的 IP 地址和端口號信息, 而 tolen 常常被賦值為sizeof (struct sockaddr)。 sendto 函數(shù)也返回實際發(fā)送的數(shù)據(jù)字節(jié)長度或在出現(xiàn)發(fā)送錯誤時返回-1。
?
2、recvfrom()函數(shù)原型:
?
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
?
from 是一個 struct sockaddr 類型的變量,該變量保存連接機的 IP 地址及端口號。fromlen 常置為 sizeof(struct sockaddr)。 當 recvfrom()返回時,fromlen 包含實際存入 from 中的數(shù)據(jù)字節(jié)數(shù)。 Recvfrom()函數(shù)返回接收到的字節(jié)數(shù)或當出現(xiàn)錯誤時返回-1, 并置相應的 errno。
?
二、淺析Socket、HTTP、SOAP、Restful、PHP、JSP、H5演變和區(qū)別
?
Socket、HTTP、SOAP 等區(qū)別
總結
以上是生活随笔為你收集整理的LINUX之网络编程j简述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 好程序员前端教程之JavaScript闭
- 下一篇: linux下Mariadb的二进制安装