本地的程序間通訊(ipc)有很多種方式,總結為4類:
在本地可以通過程序pid來唯一標識乙個程序,但是在網路中這是不可以的。tcp/ip協議族幫我們解決了這個問題,網路層的「ip位址」可以唯一標識網路中的主機,而傳輸層的「協議+埠」可以唯一標識主機中的應用程式(程序)。這樣利用三元組(ip位址、協議、埠)就可以標識網路的程序了。
使用tcp/ip協議的應用程式通常採用應用程式設計介面:unix bsd的套接字(socket)和unix system v的tli(已被淘汰),來實現網路程序之間的通訊。
socket起源於unix,而unix/linux基本哲學之一就是「一切皆檔案」,都可以用「開啟open->讀寫write/read->關閉close」模式來操作。socket就是該模式的乙個實現,是一種特殊的檔案,一些socket函式就是對其進行的操作(讀寫io,開啟,關閉)。
int socket(int domain, int type, int protocol);
socket函式對應於普通檔案的開啟操作。普通檔案的開啟操作返回乙個檔案描述字,而socket()用於建立乙個socket描述符,它唯一標識乙個socket。
bind()函式把乙個位址族中的特定位址賦給socket。例如對應af_inet、af_inet6就是把乙個ipv4或ipv6位址和埠號組合賦給socket。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
ipv4:
struct sockaddr_in ;
/* internet address. */
struct in_addr ;
ipv6對應的是:
struct sockaddr_in6 ;
struct in6_addr ;
主機位元組序就是我們平常說的大端和小端模式:不同的cpu有不同的位元組序型別,這些位元組序是指整數在記憶體中儲存的順序,這個叫做主機序。
小端:低位位元組放在記憶體的低位址端,高位位元組放在記憶體的高位址端。
大端:高位位元組放在記憶體的低位址端,低位位元組放在記憶體的高位址端。
網路位元組序:4個位元組的32bit值以下面的次序傳輸:首先是0-7bit,其次是8-15bit,然後16-23bit,最後是24-31bit。這種傳輸次序稱作大端位元組序。由於tcp/ip首部中所有的二進位制整數在網路中傳輸時都要求以這種次序,因此它又稱作網路位元組序。
所以,在將乙個位址繫結到socket的時候,請先將主機位元組序轉換成網路位元組序,而不要假定主機位元組序跟網路位元組序一樣使用的是大端。
如果作為乙個伺服器,在呼叫socket(),bind()之後就會呼叫listen()來監聽這個socket,如果客戶端這時呼叫connect()發出連線請求,伺服器端就會接收到這個請求。
socket()函式建立的socket預設是乙個主動型別的,listen函式將socket變為被動型別的,等待客戶的連線請求。
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
tcp伺服器端依次呼叫socket、bind、listen之後,就會監聽指定的socket位址了。tcp客戶端依次呼叫socket、connect之後就像tcp伺服器傳送了乙個連線請求。tcp伺服器監聽到這個請求之後,就會呼叫accept函式去接收請求,這樣連線就建立好了。之後就可以開始網路i/o操作了,即類同於普通檔案的讀寫i/o操作。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
網路i/o操作有以下幾組:
在伺服器與客戶端建立連線之後,會進行一些讀寫操作,完成了讀寫操作就要關閉相應的socket描述字。
#include
int close(int fd);
close乙個tcp socket的預設行為是把該socket標記為已關閉,然後立即返回到呼叫程序。該描述字不能再由呼叫程序使用,也就是說不能再作為read或write的第乙個引數。
注意:close操作只是相應socket描述字的引用計數-1。只有當引用計數為0的時候,才會觸發tcp客戶端向伺服器傳送終止連線請求。
客戶端向伺服器傳送乙個syn j
伺服器向客戶端響應乙個syn k,並對syn j進行確認ack j+1
客戶端再向伺服器發乙個確認ack k+1
從圖中可以看出來,當客戶端呼叫connect時,觸發了連線請求,向伺服器傳送了syn j包,這時connect進入阻塞狀態;伺服器監聽到連線請求,即收到syn j包,呼叫accept函式接收請求向客戶端傳送syn k, ack j+1,這時accept進入阻塞狀態;客戶端收到伺服器的syn k, ack j+1之後,這時connect返回,並對syn k進行確認;伺服器收到ack k+1時,accept返回。
某個應用程序首先呼叫close主動關閉連線,這時tcp傳送乙個fin m;
另一端接收到fin m之後,執行被動關閉,對這個fin進行確認。
一段時間之後,接收到檔案結束符的應用程序呼叫close關閉它的socket。也會傳送乙個fin n;
接收到這個fin的源傳送端tcp對它進行確認。
伺服器端**:
#include#include#include#include#include#include#include#define maxline 4096
int main(int argc, char** ar**)
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = af_inet;
servaddr.sin_addr.s_addr = htonl(inaddr_any);
servaddr.sin_port = htons(6666);
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
if( listen(listenfd, 10) == -1)
printf("*****=waiting for client's request*****=\n");
while(1)
n = recv(connfd, buff, maxline, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
}close(listenfd);
}
客戶端**:
#include#include#include#include#include#include#include#define maxline 4096
int main(int argc, char** ar**)
if( (sockfd = socket(af_inet, sock_stream, 0)) < 0)
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = af_inet;
servaddr.sin_port = htons(6666);
if( inet_pton(af_inet, ar**[1], &servaddr.sin_addr) <= 0)
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
printf("send msg to server: \n");
fgets(sendline, 4096, stdin);
if( send(sockfd, sendline, strlen(sendline), 0) < 0)
close(sockfd);
exit(0);
}
socket網路程式設計 學習筆記
伺服器端先初始化socket,然後與埠繫結 bind 對埠進行監聽 listen 呼叫accept阻塞,等待客戶端連線。在這時如果有個客戶端初始化乙個socket,然後連線伺服器 connect 如果連線成功,這時客戶端與伺服器端的連線就建立了。客戶端傳送資料請求,伺服器端接收請求並處理請求,然後把...
android學習筆記 Socket編
android學習筆記 socket程式設計 inonoclas 十三 socket程式設計 socket基本通訊模型 使用基於tcp協議的socket 使用基於udp協議的socket socket套接字 用於描述ip位址和埠是乙個通訊鏈的控制代碼 以你公用程式通過 套接字 向網路發出請求或者應答...
網路程式設計之SOCKET程式設計學習筆記
使用tcp ip協議的應用程式通常採用應用程式設計介面 unix bsd的套接字 socket 和unix system v的tli 已經被淘汰 來實現網路程序之間的通訊。就目前而言,幾乎所有的應用程式都是採用socket,而現在又是網路時代,網路中程序通訊是無處不在,這就是我為什麼說 一切皆soc...