系統(tǒng)運(yùn)維
之前說的用于進(jìn)程間通信的幾種方式:消息signal、管道pipe、消息隊(duì)列msg、共享內(nèi)存shm、信號量sem。都只適用于一臺主機(jī)上的進(jìn)程間通信,那么如何實(shí)現(xiàn)兩臺計(jì)算機(jī)之間的進(jìn)程通信呢?所以,來了解一下異地進(jìn)程通信。
1 異地進(jìn)程通信
協(xié)議層為雙方的主機(jī)通信進(jìn)程分配“端口”和緩沖區(qū),以便異地進(jìn)程間的通信。
1.1tcp/ip協(xié)議
以下是osi參考模型與tcp/ip參考模型的對應(yīng)關(guān)系:
1.1.1 tcp/ip協(xié)議族
tcp/ip 協(xié)議組大體上分為三部分:
1.internet 協(xié)議(ip)
2.傳輸控制協(xié)議(tcp)和用戶數(shù)據(jù)報(bào)文協(xié)議(udp)
3.處于tcp 和udp 之上的一組協(xié)議專門開發(fā)的應(yīng)用程序。它們包括:telnet,文件傳送協(xié)議(ftp),域名服務(wù)(dns)和簡單的郵件傳送程序(smtp)等許多協(xié)議。
應(yīng)用層協(xié)議
telnet
文件傳送協(xié)議(ftp和tftp)
簡單的文件傳送協(xié)議(smtp)
域名服務(wù)(dns)等協(xié)議
2 網(wǎng)絡(luò)編程基礎(chǔ)
socket標(biāo)準(zhǔn)被擴(kuò)展成window socket和unix socket
linux中的網(wǎng)絡(luò)編程通過socket接口實(shí)現(xiàn)。
socket既是一種特殊的io,它也是一種文件描述符。
一個(gè)完整的socket 都有一個(gè)相關(guān)描述{協(xié)議,本地地址,本地端口,遠(yuǎn)程地址,遠(yuǎn)程端口};每一個(gè)socket 有一個(gè)本地的唯一socket 號,由操作系統(tǒng)分配。
2.1 socket分類
流式套接字(sock_stream)
流式的套接字可以提供可靠的、面向連接的通訊流。它使用了tcp協(xié)議。tcp 保證了數(shù)據(jù)傳輸?shù)恼_性和順序性。
數(shù)據(jù)報(bào)套接字(sock_dgram)
數(shù)據(jù)報(bào)套接字定義了一種無連接的服務(wù),數(shù)據(jù)通過相互獨(dú)立的報(bào)文進(jìn)行傳輸,是無序的,并且不保證可靠,無差錯(cuò)。使用數(shù)據(jù)報(bào)協(xié)議udp協(xié)議。
原始套接字。
原始套接字允許對低層協(xié)議如ip或icmp直接訪問,主要用于新的網(wǎng)絡(luò)協(xié)議實(shí)現(xiàn)的測試等。
2.2 編程流程
tcp
udp
具體函數(shù)的用法,就自己man了。
2.2.1 套接字地址結(jié)構(gòu)
重點(diǎn)講一下套接字地址結(jié)構(gòu):
#include < netinet/in.h>struct sockaddr{unsigned short sa_family; /* address族, af_xxx */char sa_data[14]; /* 14 bytes的協(xié)議地址 */};sa_family的取值,一般來說,ipv4使用“af_inet”
sa_data包含了一些遠(yuǎn)程電腦的地址、端口和套接字的數(shù)目,里面的數(shù)據(jù)是雜溶在一起的。一般我們不用這個(gè)結(jié)構(gòu)體,因?yàn)槲覀円话闶褂玫牡刂范际莍p 端口號。比如:ip192.168.159.2 port3306 。這樣來記錄地址。所以一般使用下面這個(gè)地址結(jié)構(gòu),而知數(shù)據(jù)類型是等效的,可以互相轉(zhuǎn)換。
#include < netinet/in.h>struct sockaddr_in {short int sin_family; /* internet地址族 */unsigned short int sin_port; /* 端口號 */struct in_addr sin_addr; /* internet地址 */unsigned char sin_zero[8]; /* 添0(和struct sockaddr一樣大?。?/};2.2.2 字節(jié)序列轉(zhuǎn)換
因?yàn)槊恳粋€(gè)機(jī)器內(nèi)部對變量的字節(jié)存儲順序不同(有的系統(tǒng)是高位在前,底位在后,而有的系統(tǒng)是底位在前,高位在后 ),而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)大家是一定要統(tǒng)一順序的。所以對與內(nèi)部字節(jié)表示順序和網(wǎng)絡(luò)字節(jié)順序不同的機(jī)器,就一定要對數(shù)據(jù)進(jìn)行轉(zhuǎn)換。
htons()——“host to network short”
主機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序(對無符號短型進(jìn)行操作2bytes)
htonl()——“host to network long”
主機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序(對無符號長型進(jìn)行操作4bytes)
ntohs()——“network to host short”
網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為主機(jī)字節(jié)順序(對無符號短型進(jìn)行操作2bytes)
ntohl()——“network to host long ”
網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為主機(jī)字節(jié)順序(對無符號長型進(jìn)行操作4bytes)
2.2.3地址格式轉(zhuǎn)換
-linux提供將點(diǎn)分格式的地址轉(zhuǎn)于長整型數(shù)之間的轉(zhuǎn)換函數(shù)。
inet_addr()能夠把一個(gè)用數(shù)字和點(diǎn)表示ip 地址的字符串轉(zhuǎn)換成一個(gè)無符號長整型。
inet_ntoa()能夠把網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為地址結(jié)構(gòu)的數(shù)據(jù)。
2.2.4基本套接字調(diào)用
socket() bind() connect()
listen() accept() send()
recv() sendto() shutdown()
recvfrom() close() getsockopt()
setsockopt() getpeername()
getsockname() gethostbyname()
gethostbyaddr() getprotobyname()
fcntl()
練習(xí)1-tcp
tcp連接,等待客戶端輸入,將內(nèi)容發(fā)送給服務(wù)器,并獲取客戶端地址。
這里,getsocketname()表示獲得本地(自己)的地址;
getpeername()表示獲得連接上的客戶端的地址(源ip地址)。
<br>
server.c
#include < sys/types.h> #include < sys/socket.h>#include < netinet/in.h> //sockaddr_in#include < stdio.h>#include < string.h>int main(){ int fd; int clientfd; int ret; pid_t pid; int addrlen = 0; char acbuf[20] = ; struct sockaddr_in addr = {0}; //自己的地址 struct sockaddr_in clientaddr = {0}; //連上的客戶端的地址 //1.socket() fd = socket(pf_inet,sock_stream,0); if(fd == -1) { perror(socket); return -1; } //2.bind() addr.sin_family = af_inet; addr.sin_port = htons(1234); addr.sin_addr.s_addr = inet_addr(192.168.159.6); ret = bind(fd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in)); if(ret == -1) { perror(bind); return -1; } //3.listen() ret = listen(fd,10); if(ret == -1) { perror(listen); return -1; } //4.阻塞 等待 accept() clientfd = accept(fd,null,null); if(clientfd == -1) { perror(accept); return -1; }//獲取客戶端地址addrlen = sizeof(struct sockaddr_in);ret = getpeername(clientfd, (struct sockaddr *)&clientaddr, &addrlen);if(ret == -1){ perror(getpeername); return -1;}printf(client login.\\\\nip: %s , port: %d\\\\n,inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));//5.通信while(1){ memset(acbuf,0,20); if (read(clientfd,acbuf,20) > 0) { printf(receive: %s\\\\n,a