生活随笔
收集整理的這篇文章主要介紹了
Linux下网络编程
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Linux以其源代碼公開(kāi)聞名于世,并以其穩(wěn)定性和可靠性雄霸操作系統(tǒng)領(lǐng)域,在網(wǎng)絡(luò)應(yīng)用技術(shù)方面使用得更加廣泛。很久以來(lái)它就是Windows的重要對(duì)手之一。隨著網(wǎng)絡(luò)時(shí)代的來(lái)臨,Linux的這種優(yōu)勢(shì)已變得更加突出。本文將論述如何在Linux環(huán)境下利用Socket實(shí)現(xiàn)客戶機(jī)/服務(wù)器通信。
隨著網(wǎng)絡(luò)技術(shù)的發(fā)展,網(wǎng)絡(luò)結(jié)構(gòu)已從過(guò)去的主機(jī)/終端型、對(duì)等型發(fā)展到現(xiàn)在廣為使用的客戶機(jī)/服務(wù)器型。客戶機(jī)/服務(wù)器模型應(yīng)用十分廣泛,在Internet上WWW,E-mail,FTP等都是基于這種模型的。在面向連接的通信模式下,服務(wù)器打開(kāi)監(jiān)聽(tīng)端口,監(jiān)聽(tīng)網(wǎng)絡(luò)上其它客戶機(jī)向該服務(wù)器發(fā)出的連接請(qǐng)求,當(dāng)收到一個(gè)請(qǐng)求信號(hào)時(shí)與該客戶機(jī)建立一個(gè)連接,之后兩者進(jìn)行交互式的通信。具體步驟可這樣組織:
1.打開(kāi)一個(gè)已知的監(jiān)聽(tīng)端口,如smtp為25、pop3為110、ftp為21、telnet為23等。
2.在監(jiān)聽(tīng)端口上監(jiān)聽(tīng)客戶機(jī)的連接請(qǐng)求,如果有客戶機(jī)請(qǐng)求連接則建立一個(gè)連接線路。
4.通信完畢后關(guān)閉連接線路并繼續(xù)監(jiān)聽(tīng)客戶機(jī)的連接請(qǐng)求。
1.向指定的服務(wù)器主機(jī)及端口發(fā)出連接請(qǐng)求。
2.當(dāng)服務(wù)器建立連接線路后與服務(wù)器進(jìn)行通信。
Linux的許多特性都非常有助于網(wǎng)絡(luò)程序設(shè)計(jì):首先Linux擁有POSIX.1標(biāo)準(zhǔn)庫(kù)函數(shù),socket()、bind()、listen()這幾個(gè)庫(kù)函數(shù)可以非常方便地實(shí)現(xiàn)服務(wù)器/客戶機(jī)模型,有關(guān)這幾個(gè)庫(kù)函數(shù)的使用說(shuō)明將在后邊介紹。其次Linux的進(jìn)程管理也非常符合服務(wù)器的工作原理,所謂進(jìn)程就是程序在內(nèi)存中運(yùn)行時(shí)的狀態(tài),可以說(shuō)進(jìn)程是動(dòng)態(tài)的程序。在運(yùn)行著Linux操作系統(tǒng)的計(jì)算機(jī)中,每一個(gè)進(jìn)程都有一個(gè)創(chuàng)建它的父進(jìn)程,而且它也能創(chuàng)建多個(gè)子進(jìn)程。在服務(wù)器端我們可以用父進(jìn)程去監(jiān)聽(tīng)客戶機(jī)的連接請(qǐng)求,當(dāng)有客戶機(jī)的連接請(qǐng)求時(shí)父進(jìn)程創(chuàng)建一個(gè)子進(jìn)程去建立連接線路并與客戶機(jī)通信,而它本身可繼續(xù)監(jiān)聽(tīng)其它客戶機(jī)的連接請(qǐng)求,這樣就可避免當(dāng)有一個(gè)客戶機(jī)與服務(wù)器建立連接后服務(wù)器就不能再與其它客戶機(jī)通信的問(wèn)題。Linux的另一個(gè)特性是它秉承了UNIX設(shè)備無(wú)關(guān)性這一優(yōu)秀特征,即它通過(guò)文件描述符實(shí)現(xiàn)了統(tǒng)一的設(shè)備接口,磁盤(pán)、顯示終端、音頻設(shè)備、打印設(shè)備甚至網(wǎng)絡(luò)通信都使用統(tǒng)一的I/O調(diào)用。這三個(gè)特性將使Linux下的網(wǎng)絡(luò)程序設(shè)計(jì)變得易如反掌。上述三個(gè)特性的綜合利用將是這篇文章所要講述的真諦所在。下邊的客戶機(jī)/服務(wù)器實(shí)現(xiàn)過(guò)程可以說(shuō)明一二,注意與上文所述步驟的不同。
1.打開(kāi)一個(gè)已知的監(jiān)聽(tīng)端口。
2.在監(jiān)聽(tīng)端口上監(jiān)聽(tīng)客戶機(jī)的連接請(qǐng)求,當(dāng)有一客戶機(jī)請(qǐng)求連接時(shí)建立連接線路并返回通信文件描述符。
4.父進(jìn)程創(chuàng)建一子進(jìn)程,父進(jìn)程關(guān)閉通信文件描述符并繼續(xù)監(jiān)聽(tīng)端口上的客戶機(jī)連接請(qǐng)求。
3.子進(jìn)程通過(guò)通信文件描述符與客戶機(jī)進(jìn)行通信,通信結(jié)束后終止子進(jìn)程并關(guān)閉通信文件描述符。
1.向指定的服務(wù)器主機(jī)及端口發(fā)出連接請(qǐng)求,請(qǐng)求成功將返回通信文件描述符。
2.通過(guò)通信文件描述符與服務(wù)器進(jìn)行通信。
Linux的以下幾個(gè)庫(kù)函數(shù)是網(wǎng)絡(luò)程序設(shè)計(jì)的核心部分,它們分別是:
int socket(int domain,int type,int protocol);
此函數(shù)為通信創(chuàng)建一個(gè)端口,正常調(diào)用將返回一個(gè)文件描述符,錯(cuò)誤調(diào)用將返回-1。domain參數(shù)有兩種選擇:AF_UNIX與AF_INET,其中AF_INET為Internet通信協(xié)議。type參數(shù)也有兩種選擇:SOCK_STREAM用于TCP,SOCK_DGRAM用于UDP。protocol參數(shù)通常為0。可通過(guò)下列代碼為基于TCP協(xié)議的Internet通信建立套接口傳輸端口:
if((sock=socket(AF_INET,SOCK_STREAM,0))==-1)
perror("Could not create socket");
int bind(int s,const struct sockaddr *address,size_t address_len);
bind英文含意是關(guān)聯(lián),捆綁。其目的就是把socket返回的套接口端口與網(wǎng)絡(luò)上的物理位置相關(guān)聯(lián)。
bind正常調(diào)用返回0,出錯(cuò)返回-1。此函數(shù)有三個(gè)參數(shù):其中s為socket調(diào)用返回的文件描述符,*address設(shè)置了與網(wǎng)絡(luò)上的物理位置相關(guān)的信息,它的類(lèi)型是struct sockaddr,但在Internet上它是struct sockaddr_in。在socket.h中struct sockaddr_in定義為:
sin_family一般為AF_INET,sin_port為端口號(hào),由于使用不同字節(jié)順序的機(jī)器必須作轉(zhuǎn)換,故應(yīng)使用宏命令htons(host to network short)來(lái)轉(zhuǎn)換端口號(hào),sin_addr將置為INADDR_ANY。這三個(gè)值設(shè)置完成后*address參數(shù)才有意義。在編寫(xiě)代碼時(shí),應(yīng)先設(shè)置*address參數(shù)內(nèi)部各成員變量的值,再調(diào)用bind。
int listen(int s,int backlog);
本函數(shù)使socket端口能夠接受從客戶機(jī)來(lái)的連接請(qǐng)求,正常調(diào)用返回0,出錯(cuò)返回-1。
s參數(shù)為socket產(chǎn)生的文件描述符,backlog為所能接受客戶機(jī)的最大數(shù)目。
socket,bind,listen 三個(gè)函數(shù)的綜合調(diào)用最終在服務(wù)器上產(chǎn)生一個(gè)能接受客戶機(jī)請(qǐng)求的監(jiān)聽(tīng)文件描述符s。
int accept(int s,struct sockaddr *address,int *address_len);
當(dāng)有客戶機(jī)發(fā)出連接請(qǐng)求時(shí),此函數(shù)初始化這個(gè)連接。正常調(diào)用返回與客戶機(jī)通信的通信文件描述符,出錯(cuò)返回-1。參數(shù)s為socket調(diào)用返回的文件描述符,address將用來(lái)存儲(chǔ)客戶機(jī)的信息,此信息由accept填入,當(dāng)與客戶機(jī)連接時(shí),客戶機(jī)的地址與端口將填到此處。address_len是客戶機(jī)地址長(zhǎng)度的字節(jié)數(shù),也由accept填入。
int connect(int s,struct sockaddr *address,size_t address_len);
客戶機(jī)調(diào)用socket建立傳輸端口后,調(diào)用connect來(lái)建立與遠(yuǎn)程服務(wù)器相連的連接線路。
此函數(shù)的參數(shù)調(diào)用同bind。
in_addr_t inet_addr(const char *addstring);
此函數(shù)將字符串a(chǎn)ddstring表示的網(wǎng)絡(luò)地址(如192.168.0.1)轉(zhuǎn)換成32位的網(wǎng)絡(luò)字節(jié)序二進(jìn)制值,若成功返回32位二進(jìn)制的網(wǎng)絡(luò)字節(jié)序地址,若出錯(cuò)返回 INADDR_NONE。INADDR_NONE是32位均為1的值(即255.255.255.255,它是Internet的有限廣播地址),故如果要轉(zhuǎn)換的addstring是255.255.255.255,函數(shù)調(diào)用將失敗。
fork的作用是拷貝父進(jìn)程的內(nèi)存映象來(lái)創(chuàng)建子進(jìn)程,兩個(gè)進(jìn)程將接著fork后的指令繼續(xù)執(zhí)行。 事實(shí)上它返回兩個(gè)進(jìn)程控制號(hào),對(duì)于父進(jìn)程它返回子進(jìn)程的進(jìn)程ID,對(duì)于子進(jìn)程它返回0。
if((childpid=fork())=-1){
perror("The fork failed");
以上介紹了網(wǎng)絡(luò)編程的有關(guān)庫(kù)函數(shù)的調(diào)用方法,下面舉一個(gè)客戶機(jī)/服務(wù)器程序的小例子具體說(shuō)明如何設(shè)計(jì)網(wǎng)絡(luò)程序。本例介紹如何查看服務(wù)器上的時(shí)間和日期,由于daytime服務(wù)器的通用端口為13,客戶機(jī)程序?qū)⑼ㄟ^(guò)調(diào)用13號(hào)端口對(duì)服務(wù)器上的時(shí)間和日期進(jìn)行操作。
打開(kāi)daytime監(jiān)聽(tīng)端口;
while(客戶機(jī)與服務(wù)器成功連接——成功返回通信文件描述符)
將當(dāng)前時(shí)間寫(xiě)入通信文件描述符;
int main(int argc,char *argv[])
struct sockaddr_in servaddr;
if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
perror("Could not create socket");
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=INADDR_ANY;
servaddr.sin_port=htons(13);
if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1)
if(listen(listenfd,254)==-1)
while(communfd=accept(listenfd,(struct sockaddr*)NULL,NULL))
if((childpid=fork())==-1)
snprintf(buf,sizeof(buf),"%.24s\r\n",ctime(&tick));
write(communfd,buf,strlen(buf));
int main(int argc,char *argv[])
struct sockaddr_in servaddr;
char recieve[1024],buf[1024];
perror("Usage: client ");
if((communfd=socket(AF_INET,SOCK_STREAM,0))==-1)
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(13);
if((servaddr.sin_addr.s_addr=inet_addr(argv[1]))==INADDR_NONE)
perror("inet_addr error");
if(connect(communfd,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1)
while((n=read(communfd,recieve,1024))>0)
if(fputs(recieve,stdout)==EOF)
用gcc編譯兩個(gè)源程序分別取名為server和client,以根用戶身份運(yùn)行服務(wù)器程序(設(shè)服務(wù)器網(wǎng)絡(luò)地址為192.168.0.1):
然后運(yùn)行客戶機(jī)程序(設(shè)服務(wù)器網(wǎng)絡(luò)地址為192.168.0.1):
在客戶機(jī)上就會(huì)反映出服務(wù)器上當(dāng)前的時(shí)間如(Tue Feb 29 21:46:19 2000)。
以上程序代碼在redhat 6.0上試驗(yàn)通過(guò)。在程序代碼中有關(guān)庫(kù)函數(shù)snprintf、fputs、read、write、close的用法就不在這里說(shuō)明了,如想了解這些庫(kù)函數(shù)的調(diào)用方法可到我的網(wǎng)頁(yè)http://lzdx.yeah. net/pro_unix.html去查找。在我的網(wǎng)頁(yè)http://lzdx.yeah.net/pro_uici.html中有關(guān)于通用Internet接口(UICI)專(zhuān)用庫(kù)的介紹,通用Internet接口(UICI)利用Socket庫(kù)函數(shù)提供了一個(gè)簡(jiǎn)化的獨(dú)立于傳輸?shù)慕涌?#xff0c;它從整體上簡(jiǎn)化了網(wǎng)絡(luò)程序設(shè)計(jì)過(guò)程。有興趣的人可到那里去看看。最后祝愿我們每個(gè)人都能編寫(xiě)出自己的網(wǎng)絡(luò)程序。
總結(jié)
以上是生活随笔 為你收集整理的Linux下网络编程 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。