目錄
一,什么是網(wǎng)絡(luò)編程
二,為什么使用端口號
三,TCP協(xié)議與UDP協(xié)議
①TCP(傳輸控制協(xié)議)
②UDP(用戶數(shù)據(jù)報協(xié)議,User Data Protocol)
③總結(jié)歸納
四,Socket服務(wù)器和客戶端的開發(fā)流程
五,服務(wù)器和客戶端相關(guān)API說明
①socket()函數(shù)
②bind()函數(shù)
③listen()函數(shù)
④accept()函數(shù)
⑤客戶端的connect()函數(shù)?
⑥數(shù)據(jù)收發(fā):read() write()和send() recv()
● read() write()
● send() recv()
六,地址格式轉(zhuǎn)換相關(guān)API
①ip地址字符串和網(wǎng)絡(luò)格式轉(zhuǎn)換(inet_aton)
②端口字節(jié)序轉(zhuǎn)換(htons)
七,socket服務(wù)端和客戶端的代碼實現(xiàn)
①socket服務(wù)端代碼(無客戶端連接):
②客戶端連接服務(wù)器端:
● 服務(wù)器端代碼(server):
● 客戶端代碼(client):
● 編譯結(jié)果:
一,什么是網(wǎng)絡(luò)編程
網(wǎng)絡(luò)編程從大的方面說就是對信息的發(fā)送到接收,中間傳輸為物理線路的作用。網(wǎng)絡(luò)編程最主要的工作就是在發(fā)送端把信息通過規(guī)定好的協(xié)議進行組裝包,在接收端按照規(guī)定好的協(xié)議把包進行解析,從而提取出對應(yīng)的信息,達到通信的目的。中間最主要的就是數(shù)據(jù)包的組裝,數(shù)據(jù)包的過濾,數(shù)據(jù)包的捕獲,數(shù)據(jù)包的分析,當然最后再做一些處理,代碼、開發(fā)工具、數(shù)據(jù)庫、服務(wù)器架設(shè)和網(wǎng)頁設(shè)計這5部分都需要接觸。
二,為什么使用端口號
①一臺擁有IP地址的主機可以提供一個IP地址來實現(xiàn)。那么,主機時怎樣區(qū)分不同的網(wǎng)絡(luò)服務(wù)呢?顯然不能只靠IP地址,因為IP地址與網(wǎng)絡(luò)服務(wù)的關(guān)系是一對多的關(guān)系;
②實際上是通過“IP地址+端口號”來區(qū)分不同服務(wù)的;
③端口提供了一種訪問通道;
④服務(wù)器一般是通過知名端口號來識別的。例如,對于每個TCP/IP實現(xiàn)來說。FTP服務(wù)器的TCP端口號都是21,每個Telnet服務(wù)器的TCP端口號都是23,每個TFTP(簡單文件傳送協(xié)議)服務(wù)器的UDP端口號都是69;
三,TCP協(xié)議與UDP協(xié)議
①TCP(傳輸控制協(xié)議)
①提供IP環(huán)境下的數(shù)據(jù)可靠傳輸(一臺計算機發(fā)出的字節(jié)流會無差錯的發(fā)往網(wǎng)絡(luò)上的其他計算機,而且計算機A接收數(shù)據(jù)包的時候,也會向計算機B回發(fā)數(shù)據(jù)包,這也會產(chǎn)生部分通信量),有效流控,全雙工操作(數(shù)據(jù)在兩個方向上能同時傳遞),多路復(fù)用服務(wù),是面向連接,端到端的傳輸;
②面向連接:正式通信前必須要與對方建立連接。事先為所發(fā)送的數(shù)據(jù)開辟出連接好的通道,然后再進行數(shù)據(jù)發(fā)送,像打電話。
③TCP支持的應(yīng)用協(xié)議:Telnet(遠程登錄)、FTP(文件傳輸協(xié)議)、SMTP(簡單郵件傳輸協(xié)議)。TCP用于傳輸數(shù)據(jù)量大,可靠性要求高的應(yīng)用。
②UDP(用戶數(shù)據(jù)報協(xié)議,User Data Protocol)
1)面向非連接的(正式通信前不必與對方建立連接,不管對方狀態(tài)就直接發(fā)送,像短信,QQ),不能提供可靠性、流控、差錯恢復(fù)功能。UDP用于一次只傳送少量數(shù)據(jù),可靠性要求低、傳輸經(jīng)濟等應(yīng)用。
2) UDP支持的應(yīng)用協(xié)議:NFS(網(wǎng)絡(luò)文件系統(tǒng))、SNMP(簡單網(wǎng)絡(luò)管理系統(tǒng))、DNS(主域名稱系統(tǒng))、TFTP(通用文件傳輸協(xié)議)等。
③總結(jié)歸納
TCP:面向連接、傳輸可靠(保證數(shù)據(jù)正確性,保證數(shù)據(jù)順序)、用于傳輸大量數(shù)據(jù)(流模式)、速度慢,建立連接需要開銷較多(時間,系統(tǒng)資源)。
UDP:面向非連接、傳輸不可靠、用于傳輸少量數(shù)據(jù)(數(shù)據(jù)包模式)、速度快。
?
四,Socket服務(wù)器和客戶端的開發(fā)流程
服務(wù)器端 | 客戶端 |
①socket()。創(chuàng)建socket | ①socket()。創(chuàng)建socket |
②bind()。綁定IP地址、端口等信息到socket返回的描述符上 | ②結(jié)構(gòu)體struct sockaddr_in設(shè)置要連接的對方的IP地址和端口等屬性 |
③listen()。設(shè)置允許的最大連接數(shù) | ③connect()。連接服務(wù)器 |
④accept()。接收客戶端上來的連接 | ④read()和write(),send()和recv()。收發(fā)數(shù)據(jù) |
⑤read()和write(),send()和recv()。收發(fā)數(shù)據(jù) | ⑤close()。關(guān)閉網(wǎng)絡(luò)連接 |
⑥close()。關(guān)閉網(wǎng)絡(luò)連接 | |
注意:服務(wù)器端和客戶端須使用相同的端口號和IP地址。 |
五,服務(wù)器和客戶端相關(guān)API說明
①socket()函數(shù)
作用:創(chuàng)建socket套接字,獲取類似文件描述符的網(wǎng)絡(luò)描述符。Linux中的網(wǎng)絡(luò)編程通過Socket(套接字)接口實現(xiàn)。
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol); //返回值:成功,則返回新套接字的文件描述符。失敗返回-1,并設(shè)置errno。
▲?domain:指明所使用的協(xié)議族,通常為AF_INET,表示互聯(lián)網(wǎng)協(xié)議族(TCP/IP協(xié)議族);
? ? ?●? ?AF_INET? ? ? ? ? ?IPv4因特網(wǎng)域;
? ? ?●? ?AF_INET6? ? ? ? ?IPv6因特網(wǎng)域;
? ? ?●? ?AF_UNIX? ? ? ? ? Unix域;
? ? ?●? ?AF_ROUTE? ? ? 路由套接字;
? ? ?●? ?AF_KEY? ? ? ? ? ?密鑰套接字;
? ? ?●? ?AF_UNSPEC? ?未指定;
▲ type:指定socket的類型;
? ? ?●? ?SOCK_STREAM:流式套接字提供可靠的,面向連接的通信流;它使用TCP協(xié)議,從而保證數(shù)據(jù)傳輸?shù)恼_性和順序性;
? ? ?●? ?SOCK_DGRAM: 數(shù)據(jù)報套接字定義一種無連接的服,數(shù)據(jù)通過相互獨立的報文進行傳輸,是無序的,不能保證是可靠,無差錯,它使用UDP數(shù)據(jù)報協(xié)議;
? ? ?●? ?SOCK_RAW:允許程序使用低層協(xié)議,原始套接字允許對底層協(xié)議如IP或ICMP進行直接訪問,功能強大但使用較為不便,主要用于一些協(xié)議的開發(fā)。
▲ procolto:通常賦值‘0’,默認協(xié)議;
? ? ?●? ?"0"選擇type類型對應(yīng)的默認協(xié)議
? ? ?●? ?IPPROTO_TCP? ?TCP傳輸協(xié)議
? ? ?●? ?IPPROTO_UDP? ?UDP傳輸協(xié)議
? ? ?●? ?IPPROTO_SCTP? ?SCTP傳輸協(xié)議
? ? ?●? ?IPPROTO_TIPC? ?TIPC傳輸協(xié)議
②bind()函數(shù)
作用:綁定IP地址、端口等信息到socket返回的描述符上(socketfd)。
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); //返回值:如果成功,則返回0。發(fā)生錯誤時,返回-1。errno設(shè)置錯誤碼。
▲ sockfd:socket返回的描述符(socketfd);
▲addr:是指向包含本機IP地址及端口號信息的sockaddr類型的指針,指向綁定給sockfd的協(xié)議地址結(jié)構(gòu),這個地址結(jié)構(gòu)根據(jù)地址創(chuàng)建socket時的地址協(xié)議族的不同而不同。結(jié)構(gòu)體如下:
▲addrlen:結(jié)構(gòu)體大小sizeof();
sockaddr結(jié)構(gòu)的兩種原型:一般使用新版本struct sockaddr_in的結(jié)構(gòu)體類型 struct sockaddr?{
? ? ? ? ? ? ? ?u_short sa_family; ?// 地址族或協(xié)議族,采用“AF_xxx”的形式,如:AF_INET ?
? ? ? ??? ? ? ?char sa_data[14]; ?//? ? IP+端口, 14字節(jié)的特定協(xié)議地址
};?對應(yīng)IPv4,同等替換: struct sockaddr_in?{
? ? ? ? ? ? ? ? ? ? ? short int? ? sin_family;? ? ? ? /* 地址族或協(xié)議族 */
? ? ? ? ? ? ? ? ? ? ? unsigned short int? ? sin_port;? ? ? /* 端口號 */
? ? ? ? ? ? ? ? ? ? ? struct in_addr? ? sin_addr;? ? ? ?/* IP地址結(jié)構(gòu)體 */
? ? ? ? ? ? ? ? ? ? ? unsigned char? ? sin_zero[8];? ? ? /* 填充?,無實際意義,只是為跟sockaddr結(jié)構(gòu)在內(nèi)存中對齊,這樣兩者才能互相轉(zhuǎn)換*/};
struct in_addr{
? ? ? ? ? ? ? ? ? ? ? unsigned long? ? s_addr; ? // S_addr: 32位的地址
};linux結(jié)構(gòu)體查詢步驟:①cd /usr/include/? ? ? ?②grep "struct sockaddr_in {" * -nir? ? ? ? ?③vi linux/in.h +顯示的數(shù)字
③listen()函數(shù)
作用:監(jiān)聽套接字上的連接,設(shè)置能處理的最大連接數(shù)。不阻塞,告訴內(nèi)核服務(wù)器能處理多少個連接隊列。
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int listen(int sockfd, int backlog); //返回值:如果成功,則返回0。發(fā)生錯誤時,返回-1和errno設(shè)置正確。
▲sockfd:socket返回的描述符(socketfd);
▲backlog:設(shè)置在請求連接隊列中能連接的最大請求數(shù),直接用寫數(shù)字即可。
④accept()函數(shù)
作用:阻塞,等待接收客戶端上來的連接。由TCP服務(wù)器調(diào)用,用于從已完成連接隊列頭返回下一個已完成連接。如果已完成連接隊列為空,那么進程被投入睡眠。
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //返回值:該函數(shù)返回值是一個新的套接字描述符,返回值表示已連接的套接字描述符,而第一個參數(shù)是服務(wù)器監(jiān)聽套接字描述符。一個服務(wù)器通常僅僅創(chuàng)建一個監(jiān)聽套接字,它在該服務(wù)器的生命周期內(nèi)一直存在,內(nèi)核為每個由服務(wù)器進程接受的客戶連接創(chuàng)建一個已連接套接字(表示TCP三次握手已經(jīng)完成),當服務(wù)器完成對某個給定客戶的服務(wù)時,相應(yīng)的已連接套接字就會被關(guān)閉。失敗返回-1,設(shè)置erron
▲sockfd:服務(wù)器端調(diào)用socket返回的描述符(socketfd);
▲addr:用來返回已連接的對端(客戶端)的協(xié)議地址;
▲addrled:客戶端地址長度。
⑤客戶端的connect()函數(shù)?
作用:用于綁定后的client端(客戶端),與服務(wù)器建立連接。
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); //返回值:如果連接或綁定成功,則返回0。失敗,返回-1,并適當?shù)卦O(shè)置errno。
▲socfd:服務(wù)端socket返回的描述符;
▲addr:服務(wù)器端IP地址和端口號的地址結(jié)構(gòu)指針;(可以參考上面bind()函數(shù)中的結(jié)構(gòu)指針);
▲addrlen:地址長度被設(shè)置為sizeof(struct socaddr);
⑥數(shù)據(jù)收發(fā):read() write()和send() recv()
● read() write()
作用:在套接字通信中進行字節(jié)讀取函數(shù)。與I/O中的讀取函數(shù)略有區(qū)別,因為它們輸入或輸出的字節(jié)數(shù)可能比請求的數(shù)少。
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); //返回值:成功返回讀或?qū)懙淖止?jié)個數(shù),出錯返回-1
說明:第一個將buf中的nbytes個字節(jié)寫入到文件描述符fd中,成功時返回寫的字節(jié)數(shù);第二個從fd中讀取nbytes個字節(jié)到buf中,返回實際所讀的字節(jié)數(shù)。
● send() recv()
作用:在TCP套接字上發(fā)送和接收數(shù)據(jù)函數(shù):有連接
#include <sys/types.h> #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags); //包含3要素:套接字s,待發(fā)數(shù)據(jù)msg,數(shù)據(jù)長度len; //函數(shù)只能對處于連接狀態(tài)的套接字使用,參數(shù)s為已讀建立好連接的套接字描述符,即accept函數(shù)的返回值; //參數(shù)msg指向存放待發(fā)送數(shù)據(jù)的緩沖區(qū); //參數(shù)len為待發(fā)送數(shù)據(jù)的長度,參數(shù)flag為控制選項,一般設(shè)置為0; ssize_t recv(int sockfd, void *buf, size_t len, int flags); //包含3要素:套接字s,接收緩沖區(qū)buf,長度len; //函數(shù)recv從參數(shù)s所指定的套接字描述符(必須面向連接的套接字)上接收數(shù)據(jù)并保存到buf所指定的緩沖區(qū); //參數(shù)len為緩沖區(qū)長度,參數(shù)flags為控制選項,一般設(shè)置為0;
六,地址格式轉(zhuǎn)換相關(guān)API
①ip地址字符串和網(wǎng)絡(luò)格式轉(zhuǎn)換(inet_aton)
說明:IP地址通常由數(shù)字加點(192.168.0.0)的形式表示,而在struct in_addr中使用的是IP地址是由32位的整數(shù)表示的,轉(zhuǎn)換函數(shù)如下:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *cp,struct in_addr *inp) //將字符串形式的“000.000.0.0”形式的IP轉(zhuǎn)換為32位的IP(網(wǎng)絡(luò)能識別的格式),存儲在inp指針里面。 char *inet_ntoa(struct in_addr in) //是將32位IP(網(wǎng)絡(luò)格式)轉(zhuǎn)換為字符串“000.000.0.0”的格式
②端口字節(jié)序轉(zhuǎn)換(htons)
說明:不同類型的 CPU 對變量的字節(jié)存儲順序可能不同:有的系統(tǒng)是高位在前,低位在后,而有的系統(tǒng)是低位在前,高位在后,而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)順序是一定要統(tǒng)一的。所以當內(nèi)部字節(jié)存儲順序和網(wǎng)絡(luò)字節(jié)順序不同時,就一定要進行轉(zhuǎn)換。網(wǎng)絡(luò)字節(jié)順序采用big endian(大端)排序方式。
#include <arpa/inet.h> uint32_t htonl(uint32_t hostlong);//把unsigned long類型從主機序轉(zhuǎn)換到網(wǎng)絡(luò)序 uint16_t htons(uint16_t hostshort);//把unsigned short類型從主機序轉(zhuǎn)換到網(wǎng)絡(luò)序// uint32_t ntohl(uint32_t netlong);//把unsigned long類型從網(wǎng)絡(luò)序轉(zhuǎn)換到主機序 uint16_t ntohs(uint16_t netshort);//把unsigned short類型從網(wǎng)絡(luò)序轉(zhuǎn)換到主機序
函數(shù)說明:①h表示host,n表示network,l表示long(4字節(jié)),s表示short(2字節(jié))。例如htonl表示將32位的長整數(shù)從主機字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,例如將IP地址轉(zhuǎn)換后準備發(fā)送。如果主機是小端字節(jié)序,這些函數(shù)將參數(shù)做相應(yīng)的大小端轉(zhuǎn)換然后返回,如果主機是大端字節(jié)序,這些函數(shù)不做轉(zhuǎn)換,將參數(shù)原封不動地返回。
? ? ? ? ? ? ? ? ? ②根據(jù)情況使用NADDR_ANY,INADDR_ANY指定地址讓操作系統(tǒng)自己獲取。
七,socket服務(wù)端和客戶端的代碼實現(xiàn)
①socket服務(wù)端代碼(無客戶端連接):
#include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <string.h> int main(void) { //1 socket 創(chuàng)建套接字,獲取描述符 int socfd = socket(AF_INET,SOCK_STREAM,0); if(socfd == -1){ perror("error socket"); } //結(jié)構(gòu)體struct sockaddr_in設(shè)置要連接的對方的IP地址和端口等屬性 struct sockaddr_in s_addr; s_addr.sin_family = AF_INET; s_addr.sin_port = htons(7777); inet_aton("127.0.0.1",&s_addr.sin_addr); //2 bind 綁定IP地址、端口等信息到socket返回的描述符上 bind(socfd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)); //3 listen 設(shè)置允許的最大連接數(shù) listen(socfd,10); //4 阻塞 等accept接收客戶端上來的連接請求 struct sockaddr_in c_addr; int clen = sizeof(struct sockaddr_in); int c_fd = accept(socfd,(struct sockaddr *)&c_addr,&clen); if(c_fd == -1){ perror("error accept!\n"); } //5 read 讀取來自客戶端上的請求信息 char readBuf[128] = "masseag from client!"; int n_read = read(c_fd,readBuf,128); if(n_read == -1){ perror("read\n"); }else{ printf("read%dByte:%s\n",n_read,readBuf); } //6 write 收到客戶端的請求信息后,回復(fù)信息給客戶端 char *msg = "I got your masseag!"; write(c_fd,msg,strlen(msg)); //7 關(guān)閉網(wǎng)絡(luò)連接 close(c_fd); return 0; }
?編譯結(jié)果:telnet 遠程登陸協(xié)議
文章來源:http://www.zghlxwxcb.cn/news/detail-667243.html
②客戶端連接服務(wù)器端:
● 服務(wù)器端代碼(server):
? #include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> int main(void) { //1,socket 創(chuàng)建套接字,獲取描述符 int sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd == -1){ perror("socket"); exit(-1); } //2,bind 綁定IP地址和端口信息 struct sockaddr_in s_addr; s_addr.sin_family = AF_INET; s_addr.sin_port = htons(6666);//字節(jié)序轉(zhuǎn)換 inet_aton("127.0.0.1",&s_addr.sin_addr);//IP地址格式轉(zhuǎn)換 bind(sockfd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)); //3,listen 設(shè)置監(jiān)聽數(shù)量,告訴內(nèi)核本服務(wù)器能連接客戶段的最大數(shù)量 listen(sockfd,1); //4,accept 接受套接字的連接 int clen = sizeof(struct sockaddr_in); int c_fd = accept(sockfd,(struct sockaddr *)&s_addr,&clen); if(c_fd == -1){ perror("accept"); exit(-1); } //5,read 服務(wù)器會先讀到來至客戶端的請求 char readBuf[128]; int n_read = read(c_fd,readBuf,128); if(n_read == -1){ perror("read"); }else{ printf("read%dByte:%s\n",n_read,readBuf); } //6,write 服務(wù)器在收到客戶端的請求后,會回信給客戶端 char *msg= "ok,I got your message!"; write(c_fd,msg,strlen(msg)); //7,關(guān)閉網(wǎng)絡(luò)連接 close(c_fd); return 0; } ?
● 客戶端代碼(client):
#include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <unistd.h> int main(void) { //1,socket 創(chuàng)建套接字,獲取描述符 int sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd == -1){ perror("sockfd"); exit(-1); } //結(jié)構(gòu)體struct sockaddr_in設(shè)置要連接的對方的IP地址和端口等屬性 struct sockaddr_in s_addr; s_addr.sin_family = AF_INET; s_addr.sin_port = htons(6666); inet_aton("127.0.0.1",&s_addr.sin_addr); //2,connect 連接服務(wù)器 if(connect(sockfd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr)) == -1){ perror("connect"); } //3,向服務(wù)器發(fā)送請求 char buf[128] = "I set your message"; write(sockfd,buf,strlen(buf)); //4,讀取來自服務(wù)器的回信 char msg[128]; int n_read = read(sockfd,msg,128); if(n_read == -1){ perror("read"); }else{ printf("read%dByte:%s\n",n_read,msg); } //5,關(guān)閉網(wǎng)絡(luò)連接 close(sockfd); return 0; }
● 編譯結(jié)果:
文章來源地址http://www.zghlxwxcb.cn/news/detail-667243.html
到了這里,關(guān)于Linux網(wǎng)絡(luò)編程:Socket服務(wù)器和客戶端實現(xiàn)雙方通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!