版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
-rLINUX網(wǎng)輅編程
LINUXTCP/IP網(wǎng)絡(luò)編程
3匚大,堂信息科考與工程學(xué)院付沖
LINUX網(wǎng)輅編程
LinuxTCP/IP網(wǎng)絡(luò)編程
1.1Linux網(wǎng)絡(luò)基礎(chǔ)
TCP/IP協(xié)議集
TELNETFTPSMTPDNSHTTPSNMPPOPApplication
TCPUDPTransport
ICMP
IPInternet
ARPRARP
Host-to-
ARPANETSATNETPacketradioLAN
network
LINUX網(wǎng)輅編程
L本地回路:lo,固定IP地址:
以太網(wǎng)卡:ethO,ethl,.......
2.查看IP地址一ifconfig
各字段說明:
Hwaddr:網(wǎng)卡地址(物理地址,硬件地址)
inetaddr:IP地址
Beast:廣播地址
LINUX網(wǎng)輅編程
Mask:子網(wǎng)掩碼
MTU:MaxTransmissionUnit,最大傳輸單元,
即數(shù)據(jù)幀最大長度
Metric:路由長度
TXpackets:發(fā)送的數(shù)據(jù)包總數(shù)、錯誤數(shù)、遺失數(shù)
及溢出數(shù)
RXpackets:接收的數(shù)據(jù)包總數(shù)、錯誤數(shù)、遺失數(shù)
及溢出數(shù)
LINUX網(wǎng)輅編程
3.設(shè)置IP地址相關(guān)信息
語法:ifconfig接口IP地址[broadcast廣播
地址netmask子網(wǎng)掩碼]
例:ifconfigethO
ifconfigethObroadcast
55netmask
注意:ifconfig命令設(shè)置的IP地址不能永久保存,重
新啟動計算機后將丟失設(shè)置
LINUX網(wǎng)輅編程
4.netconfig命令設(shè)置網(wǎng)絡(luò)參數(shù)
usedynamicIPconfiguration
IPaddress
Netmask
Defaultgateway(IP)
Primarynameserver
注意:netconfig命令設(shè)置的網(wǎng)絡(luò)配置信息可以永久
保存到配置文件中,但不會立即生效,刷新方
法:/etc/init.d/networkrestart
LINUX網(wǎng)輅編程
5.網(wǎng)絡(luò)配置相關(guān)文件
(1)/etc/sysconfig/network
HOSTNAME:主機名設(shè)置
GATEWAY:默認(rèn)網(wǎng)關(guān)的設(shè)置
LINUX網(wǎng)輅編程
(2)/etc/sysconng/network-scnpts/ifcfg-ethO
DEVICE:設(shè)備名稱
BOOTPROTO:IP地址設(shè)置方式(動態(tài)或靜態(tài))
BROADCAST:廣播地址
HWADDR:硬件地址
IPADDR:IP地址設(shè)置
NETMASK:子網(wǎng)掩碼設(shè)置
NETWORK:網(wǎng)絡(luò)地址
ONBOOT:啟動時是否激活
LINUX網(wǎng)輅編程
(3)/etc/resolv.conf
DNS服務(wù)器地址設(shè)置。
(4)/etc/hosts
存放一些常用主機的IP地址與主機名稱的數(shù)據(jù),
即一個簡單的DNS數(shù)據(jù)庫。
基本格式為:
IP主機域名主機別名
(5)/etc/services
記錄主機提供的網(wǎng)絡(luò)服務(wù)項目、端口號(port)及
使用的運輸層協(xié)議。
LINUX網(wǎng)輅編程
1.2套接字編程基本概念
在UNIX系統(tǒng)中,網(wǎng)絡(luò)應(yīng)用編程接口有兩類:
UNIXBSD的套接字(socket)
UNIXSystemV的TLI
由于Sim公司采用了支持TCP/IP的UNIXBSD操
作系統(tǒng),使TCP/IP的應(yīng)用有更大的發(fā)展,其網(wǎng)絡(luò)應(yīng)
用編程接口---套接字(socket)在網(wǎng)絡(luò)軟件中被廣泛應(yīng)
用,至今已引進(jìn)微機操作系統(tǒng)DOS和Windows系統(tǒng)
中,成為開發(fā)網(wǎng)絡(luò)應(yīng)用軟件的強有力工具。
LINUX網(wǎng)輅編程
1.2.1網(wǎng)間進(jìn)程通信
進(jìn)程通信的概念最初來源于單機系統(tǒng)。由于每個進(jìn)
程都在自己的地址范圍內(nèi)運行,為保證兩個相互通信
的進(jìn)程之間既互不干擾又協(xié)調(diào)一致工作,操作系統(tǒng)為進(jìn)
程通信提供了相應(yīng)設(shè)施,如UNIXBSD中的管道
(pipe)、命名管道(namedpipe)和軟中斷信號(signal),
UNIXsystemV的消息(message)、共享存儲區(qū)(shared
memory)和信號量(semaphore)等,但都僅限于用在本
機進(jìn)程之間通信。
LINUX網(wǎng)輅編程
網(wǎng)間進(jìn)程通信要解決的是不同主機進(jìn)程間的相互通信問
題。
為此,首先要解決的是網(wǎng)間進(jìn)程標(biāo)識問題。同一主機
上,不同進(jìn)程可用進(jìn)程號(processID)唯一標(biāo)識。但
在網(wǎng)絡(luò)環(huán)境下,各主機獨立分配的進(jìn)程號不能唯一標(biāo)識
該進(jìn)程。例如,主機A賦于某進(jìn)程號5,在B機中也可
以存在5號進(jìn)程,因此,“5號進(jìn)程”這句話就沒有意義
了。
其次,操作系統(tǒng)支持的網(wǎng)絡(luò)協(xié)議眾多,不同協(xié)議的工
作方式不同,地址格式也不同。
LINUX網(wǎng)輅編程
因此,網(wǎng)間進(jìn)程通信還要解決多重協(xié)議的識別問
題。
為了解決上述問題,TCP/IP協(xié)議引入了下列幾個概
念。
⑴端口
網(wǎng)絡(luò)中可以被命名和尋址的通信端口,是操作系
統(tǒng)可分配的一種資源。
LINUX網(wǎng)輅編程
按照OSI七層協(xié)議的描述,運輸層與網(wǎng)絡(luò)層在功能
上的最大區(qū)別是運輸層提供進(jìn)程通信能力。從這個意
義上講,網(wǎng)絡(luò)通信的最終地址就不僅僅是主機地址
了,還包括可以描述進(jìn)程的某種標(biāo)識符。為此,
TCP/IP協(xié)議提出了協(xié)議端口(protocolport),簡稱端
口)的概念,用于標(biāo)識通信的進(jìn)程。
端口是一種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和
DO緩沖區(qū))。
LINUX網(wǎng)輅編程
應(yīng)用程序(即進(jìn)程)通過系統(tǒng)調(diào)用與某端口建立連接
(binding)后,運輸層傳給該端口的數(shù)據(jù)都被相應(yīng)進(jìn)程
所接收,相應(yīng)進(jìn)程發(fā)給運輸層的數(shù)據(jù)都通過該端口輸
出。
在TCP/IP協(xié)議的實現(xiàn)中,端口操作類似于一般的
DO操作,進(jìn)程獲取一個端口,相當(dāng)于獲取本地唯一的
DO文件,可以用一般的讀寫原語訪問之。
LINUX網(wǎng)輅編程
類似于文件描述符,每個端口都擁有一個叫端口號
(portnumber)的整數(shù)型標(biāo)識符,用于區(qū)別不同端口。
由于TCP/IP運輸層的兩個協(xié)議TCP和UDP是完全獨立
的兩個軟件模塊,因此各自的端口號也相互獨立,如
TCP有一個255號端口,UDP也可以有一個255號端
口,二者并不沖突。
LINUX網(wǎng)輅編程
TCP/IP號端口號分為兩部分,少量的作為保留端
口,以全局方式分配給服務(wù)進(jìn)程。因此,每一個標(biāo)準(zhǔn)
服務(wù)器都擁有一個全局公認(rèn)的端口(即周知端口,
well-knownport),即使在不同機器上,其端口號也
相同。剩余的為自由端口,以本地方式進(jìn)行分配。
TCP和UDP均規(guī)定,小于256的端口號才能作保留端
口八
LINUX網(wǎng)輅編程
⑵地址
網(wǎng)絡(luò)通信中通信的兩個進(jìn)程分別在不同的機器上。
在互連網(wǎng)絡(luò)中,兩臺機器可能位于不同的網(wǎng)絡(luò),這些
網(wǎng)絡(luò)通過網(wǎng)絡(luò)互連設(shè)備(網(wǎng)關(guān),網(wǎng)橋,路由器等)連
接。因此需要三級尋址:
(a)某一主機可與多個網(wǎng)絡(luò)相連,必須指定一特定網(wǎng)絡(luò)
地址;
LINUX網(wǎng)輅編程
(b)網(wǎng)絡(luò)上母一臺主機應(yīng)有其唯一的地址;
(c)每一主機上的每一進(jìn)程應(yīng)有在該主機上的唯一標(biāo)識
符。
通常主機地址由網(wǎng)絡(luò)ID和主機ID組成,在TCP/IP
協(xié)議中用32位整數(shù)值表示;TCP和UDP均使用16位
端口號標(biāo)識用戶進(jìn)程。
LINUX網(wǎng)輅編程
(3)網(wǎng)絡(luò)字節(jié)順序
不同的計算機存放多字節(jié)值的順序不同,有的機器
在起始地址存放低位字節(jié)(低位先存),有的存高位
字節(jié)(高位先存)。為保證數(shù)據(jù)的正確性,在網(wǎng)絡(luò)協(xié)
議中須指定網(wǎng)絡(luò)字節(jié)順序。TCP/IP協(xié)議使用16位整數(shù)
和32位整數(shù)的高位先存格式,它們均含在協(xié)議頭文件
中。
LINUX網(wǎng)輅編程
(4)半相關(guān)
綜上所述,網(wǎng)絡(luò)中用一個三元組可以在全局唯一標(biāo)
志一個進(jìn)程:
(協(xié)議,本地地址,本地端口號)
這樣一個三元組,叫做一個半相關(guān)(half-
association),它指定連接的每半部分。
LINUX網(wǎng)輅編程
(5)全相關(guān)
一個完整的網(wǎng)間進(jìn)程通信需要由兩個進(jìn)程組成,并
且只能使用同一種高層協(xié)議。也就是說,不可能通信
的一端用TCP協(xié)議,而另一端用UDP協(xié)議。因此一
個完整的網(wǎng)間通信需要一個五元組來標(biāo)識:
(協(xié)議,本地地址,本地端口號,遠(yuǎn)地地址,遠(yuǎn)地端口
號)
LINUX網(wǎng)輅編程
這樣一個五元組,叫做一個相關(guān)(association),即兩
個協(xié)議相同的半相關(guān)才能組合成一個合適的相關(guān),或
完全指定組成一連接。
LINUX網(wǎng)輅編程
1.2.2客戶/服務(wù)器模式
在TCP/IP網(wǎng)絡(luò)應(yīng)用中,通信的兩個進(jìn)程間相互作
用的主要模式是客戶/服務(wù)器模式(Client/Server
model),即客戶向服務(wù)器發(fā)出服務(wù)請求,服務(wù)器接收
到請求后,提供相應(yīng)的服務(wù)。
客戶/服務(wù)器模式的建立基于以下兩點:首先,建立
網(wǎng)絡(luò)的起因是網(wǎng)絡(luò)中軟硬件資源、運算能力和信息不
均等,需要共享,從而造就擁有眾多資源的主機提供
服務(wù),資源較少的客戶請求服務(wù)這一非對等作用。
LINUX網(wǎng)輅編程
其次,網(wǎng)間進(jìn)程通信完全是異步的,相互通信的進(jìn)
程間既不存在父子關(guān)系,又不共享內(nèi)存緩沖區(qū),因此
需要一種機制為希望通信的進(jìn)程間建立聯(lián)系,為二者
的數(shù)據(jù)交換提供同步,這就是基于客戶/服務(wù)器模式的
TCP/IPo
LINUX網(wǎng)輅編程
客戶/服務(wù)器模式在操作過程中采取的是主動請求方
式:
首先服務(wù)器方要先啟動,并根據(jù)請求提供相應(yīng)服
務(wù):
(1)打開一通信通道并告知本地主機,它愿意在某
一公認(rèn)地址上(周知口,如FTP為21)接收客戶請求;
(2)等待客戶請求到達(dá)該端口;
LINUX網(wǎng)輅編程
(3)接收到重復(fù)服務(wù)請求,處理該請求并發(fā)送應(yīng)答信
號。接收到并發(fā)服務(wù)請求,要激活一新進(jìn)程來處理這
個客戶請求(如UNIX系統(tǒng)中用fork,exec)。新進(jìn)程處
理此客戶請求,并不需要對其它請求作出應(yīng)答。服務(wù)
完成后,關(guān)閉此新進(jìn)程與客戶的通信鏈路,并終止。
(4)返回第二步,等待另一客戶請求。
(5)關(guān)閉服務(wù)器。
LINUX網(wǎng)輅編程
客戶方:
1.打開一通信通道,并連接到服務(wù)器所在主機的
特定端口;
2.向服務(wù)器發(fā)服務(wù)請求報文,等待并接收應(yīng)答;
繼續(xù)提出請求……
3.請求結(jié)束后關(guān)閉通信通道并終止。
LINUX網(wǎng)輅編程
從上面所描述過程可知:
1.客戶與服務(wù)器進(jìn)程的作用是非對稱的,因此編碼
不同。
2.服務(wù)進(jìn)程一般是先于客戶請求而啟動的。只要系
統(tǒng)運行,該服務(wù)進(jìn)程一直存在,直到正?;驈娖冉K
止。
LINUX網(wǎng)輅編程
1.2.3套接字類型
TCP/IP的socket提供下列三種類型套接字。
1.流式套接字(SOCK_STREAM)
提供了一個面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)
無差錯、無重復(fù)地發(fā)送,且按發(fā)送順序接收。內(nèi)設(shè)流
量控制,避免數(shù)據(jù)流超限;數(shù)據(jù)被看作是字節(jié)流,無
長度限制。文件傳送協(xié)議(FTP)即使用流式套接字。
LINUX網(wǎng)輅編程
2.數(shù)據(jù)報式套接字(SOCK_DGRAM)
提供了一個無連接服務(wù)。數(shù)據(jù)包以獨立包形式被發(fā)
送,不提供無錯保證,數(shù)據(jù)可能丟失或重復(fù),并且接
收順序混亂。網(wǎng)絡(luò)文件系統(tǒng)(NFS)使用數(shù)據(jù)報式套接
字。
3.原始式套接字(SOCK_RAW)
該接口允許對較低層協(xié)議,如IP、ICMP直接訪問。
常用于檢驗新的協(xié)議實現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新
設(shè)備。
LINUX網(wǎng)輅編程
1.3基本套接字系統(tǒng)調(diào)用
為了更好地說明套接字編程原理,下面給出幾個基
本套接字系統(tǒng)調(diào)用說明。
1.3.1創(chuàng)建套接字——socket()
應(yīng)用程序在使用套接字前,首先必須擁有一個套接
字,系統(tǒng)調(diào)用socket。向應(yīng)用程序提供創(chuàng)建套接字的手
段,其調(diào)用格式如下:
LINUX網(wǎng)輅編程
mtsocket(mtdomain,mttype,intprotocol)
domain:說明我們網(wǎng)絡(luò)程序所在的主機采用的通信
協(xié)議族(AFJJNIX和AFJNET等).UNIX系統(tǒng)支持的地
址族有:AFJJNIX、AFJNET>AF_NS等,而DOS、
WINDOWS中僅支持AF_INET,它是網(wǎng)際網(wǎng)區(qū)域。
AF_UNIX只能夠用于單一的Unix系統(tǒng)進(jìn)程間通信,而
AF_INET是針對Internet的,因而可以允許在遠(yuǎn)程主機
之間通信。
LINUX網(wǎng)輅編程
type:網(wǎng)絡(luò)程序所采用的通信協(xié)議(SOCK_STREAM,
SOCK_DGRAM等)。
SOCK_STREAM表明采用的是TCP協(xié)議,即提供
按順序的,可靠,雙向,面向連接的比特流。
SOCK_DGRAM表明采用的是UDP協(xié)議,這樣只
會提供定長的,不可靠,無連接的通信。
LINUX網(wǎng)輅編程
protocol:由于我們指定了type,所以這個地方我們一
般只要用0來代替就可以了socket為網(wǎng)絡(luò)通信做基本
的準(zhǔn)備.
成功時返回文件描述符,失敗時返回查看errno可
知道出錯的詳細(xì)情況.
根據(jù)這三個參數(shù)建立一個套接字,并將相應(yīng)的資源
分配給它,同時返回一個整型套接字號。
因此,socket。系統(tǒng)調(diào)用實際上指定了相關(guān)五元組中
的“協(xié)議”這一元。
LINUX網(wǎng)輅編程
1.3.2指定本地地址——bind()
當(dāng)一個套接字用socket。創(chuàng)建后,存在一個名字空間
(地址族),但它沒有被命名。bind。將套接字地址(包括
本地主機地址和本地端口地址)與所創(chuàng)建的套接字號
聯(lián)系起來,即將名字賦予套接字,以指定本地半相
關(guān)。其調(diào)用格式如下:
LINUX網(wǎng)輅編程
mtbind(mtsockfd,structsockaddr*my_addr,mt
addrlen)
sockfd:是由socket調(diào)用返回的文件描述符.
addrlen:是sockaddr結(jié)構(gòu)的長度.
my_addr:是一個指向sockaddr的指針.
sockaddr的定義:
LINUX網(wǎng)輅編程
structsockaddr{
unisgnedshortas_family;
charsa_data[14];
);
不過由于系統(tǒng)的兼容性,我們一般使用另外一個結(jié)構(gòu)
(structsockaddr_in)來代替.
sockaddr_in的定義:
LINUX網(wǎng)輅編程
structsockaddr_in{
shortsin_family;
u_shortsin_port;/*16位端口號,網(wǎng)絡(luò)字節(jié)順序*/
structin_addrsin_addr;/*32位IP地址,網(wǎng)絡(luò)字節(jié)順序刃
charsin_zero[8];/*保留刃
)
我們主要使用Internet所以sin_family一般為AF_INET,
sinaddr設(shè)置為INADDRANY表示可以和任何的主機
LINUX網(wǎng)輅編程
通信,sin_port是我們要監(jiān)聽的端口號.sin_zero⑻是用
來填充的.bind1尋本地的端口同socket返回的文件描述
符捆綁在一起.成功是返回0,失敗的情況和socket一
樣。
LINUX網(wǎng)輅編程
1.3.3建立套接字連接一connect()與accept()
這兩個系統(tǒng)調(diào)用用于完成一個完整相關(guān)的建立,其中
connect。用于建立連接。無連接的套接字進(jìn)程也可以調(diào)
用connect(),但這時在進(jìn)程之間沒有實際的報文交換,
調(diào)用將從本地操作系統(tǒng)直接返回。而accept。用于使服
務(wù)器等待來自某客戶進(jìn)程的實際連接。
connect。的調(diào)用格式如下:
intconnect(intsockfd,structsockaddr^serv_addr9int
addrlen)
LINUX網(wǎng)輅編程
參數(shù)sockfd是欲建立連接的本地套接字描述符。參
數(shù)serv_addr指出說明對方套接字地址結(jié)構(gòu)的指針。
對方套接字地址長度由addrlen說明。
connect函數(shù)是客戶端用來同服務(wù)端連接的。成功時
返回0,sockfd是同服務(wù)端通信的文件描述符,失敗時
返回
LINUX網(wǎng)輅編程
accept。的調(diào)用格式如下:
intaccept(intsockfd,structsockaddr*addr,int
^addrlen)
accept()調(diào)用前應(yīng)該先調(diào)用過listen()o
sockfd:是listen后的文件描述符。
addr,addrlen是用來給客戶端的程序填寫的,服務(wù)
器端只要傳遞指針就可以了。
LINUX網(wǎng)輅編程
調(diào)用前,參數(shù)addr指向一個初始值為空的地址結(jié)
構(gòu),而addrlen的初始值為0;調(diào)用accept()后,服務(wù)
器等待從編號為sockfd的套接字上接受客戶連接請
求,而連接請求是由客戶方的connect()調(diào)用發(fā)出的。
當(dāng)有連接請求到達(dá)時,accept()調(diào)用將請求連接隊列上
的第一個客戶方套接字地址及長度放入addr和addrlen.
LINUX網(wǎng)輅編程
bind,listen和accept是服務(wù)器端用的函數(shù),accept調(diào)
用時,服務(wù)器端的程序會一直阻塞到有一個客戶程序發(fā)
出了連接。accept成功時返回最后的服務(wù)器端的文件
描述符,這個時候服務(wù)器端可以向該描述符寫信息
了,失敗時返回4。
四個套接字系統(tǒng)調(diào)用,socket。、bind。、connect。、
accept(),可以完成一個完全五元相關(guān)的建立。socket()
指定五元組中的協(xié)議元,它的用法與是否為客戶或服
務(wù)器、是否面向連接無關(guān)。
LINUX網(wǎng)輅編程
bind()指定五元組中的本地二元,即本地主機地址和
端口號,其用法與是否面向連接有關(guān):在服務(wù)器方,
無論是否面向連接,均要調(diào)用bind。;在客戶方,若采
用面向連接,則可以不調(diào)用bind(),而通過connect。自
動完成。若采用無連接,客戶方必須使用bind。以獲得
一個唯一的地址。
LINUX網(wǎng)輅編程
1.3.4監(jiān)聽連接——listen()
此調(diào)用用于面向連接服務(wù)器,表明它愿意接收連
接。listen。需在accept。之前調(diào)用,其調(diào)用格式如下:
intlisten(intsockfd,intbacklog)
參數(shù)sockfd標(biāo)識一個本地已建立、尚未連接的套接
字號,服務(wù)器愿意從它上面接收請求。
LINUX網(wǎng)輅編程
backlog表示請求連接隊列的最大長度,用于限制排
隊請求的個數(shù),目前允許的最大值為5。如果沒有錯誤
發(fā)生,listen。返回0。否則它返回SOCKET_ERROR。
調(diào)用listen。是服務(wù)器接收一個連接請求的四個步驟
中的第三步。它在調(diào)用socket。分配一個流套接字,且
調(diào)用bind。給sockfd賦于一個名字之后調(diào)用,而且一
定要在accept。之前調(diào)用。
LINUX網(wǎng)輅編程
在客戶/服務(wù)器模式中,有兩種類型的服務(wù):重復(fù)服
務(wù)和并發(fā)服務(wù)。accept。調(diào)用為實現(xiàn)并發(fā)服務(wù)提供了極
大方便,因為它要返回一個新的套接字號,其典型結(jié)
構(gòu)為:
intinitsockid,newsockid;
if((initsockid=socket(....))<0)
error(ucan,tcreatesocket");
if(bind(initsockidv...)<0)
error(ubinderror");
LINUX網(wǎng)輅編程
if(listen(initsockid,5)v0)
error(ulistenerror");
for(;;){
newsockid=accept(initsockid,)/*阻塞*/
if(newsockid<0)
error(uaccepterror");
if(fork()==0){/*子進(jìn)程*/
closesocket(initsockid);
do(newsockid);/*處理請求*/
exit(0);
LINUX網(wǎng)輅編程
closesocket(newsockid);/*父進(jìn)程*/
)
這段程序執(zhí)行的結(jié)果是newsockid與客戶的套接字建
立相關(guān),子進(jìn)程啟動后,關(guān)閉繼承下來的主服務(wù)器的
initsockid,并利用新的newsockid與客戶通信。主服務(wù)
器的initsockid可繼續(xù)等待新的客戶連接請求。由于在
Unix等搶先多任務(wù)系統(tǒng)中,在系統(tǒng)調(diào)度下,多個進(jìn)程
可以同時進(jìn)行。因此,使用并發(fā)服務(wù)器可以使服務(wù)器
LINUX網(wǎng)輅編程
進(jìn)程在同一時間可以有多個子進(jìn)程和不同的客戶程序
連接、通信。在客戶程序看來,服務(wù)器可以同時并發(fā)
地處理多個客戶的請求,這就是并發(fā)服務(wù)器名稱的來
由。
LINUX網(wǎng)輅編程
面向連接服務(wù)器也可以是重復(fù)服務(wù)器,其結(jié)構(gòu)如下:
intinitsockid,newsockid;
if((initsockid=socket(....))<0)
error(ucan,tcreatesocket");
if(bind(initsockidv...)<0)
error(ubinderror");
if(listen(initsockid,5)<0)
error(ulistenerror");
LINUX網(wǎng)輅編程
for(;;){
newsockid=accept(initsockid,...)/*阻塞?/
if(newsockid<0)
error(uaccepterror");
do(newsockid);/*處理請求*/
closesocket(newsockid);
)
LINUX網(wǎng)輅編程
重復(fù)服務(wù)器在一個時間只能和一個客戶程序建立連
接,它對多個客戶程序的處理是采用循環(huán)的方式重復(fù)
進(jìn)行,因此叫重復(fù)服務(wù)器。
并發(fā)服務(wù)器和重復(fù)服務(wù)器各有利弊:并發(fā)服務(wù)器可
以改善客戶程序的響應(yīng)速度,但它增加了系統(tǒng)調(diào)度的
開銷;重復(fù)服務(wù)器正好與其相反,因此用戶在決定是
使用并發(fā)服務(wù)器還是重復(fù)服務(wù)器時,要根據(jù)應(yīng)用的實
際情況來定。
服務(wù)器方
LINUX網(wǎng)輅編程
客戶機方
建立連接
服務(wù)請求/應(yīng)答
LINUX網(wǎng)輅編程
1.4基本套接字通信程序?qū)嵗?/p>
服務(wù)器端(server-basic.c):
#include<stdio.h>
#include<errno.h>
#include<netdb.h>
#include<sys/socket.h>
intmain(intargc,char*argv[]){
intsockfd,new_fd;
structsockaddr_inserver_addr;
structsockaddr_inclient_addr;
LINUX網(wǎng)輅編程
intsin_size,portnumber;
charhello[]=HHello!AreYouFine?\nM;
if(argc!=2){
H1f
fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);
exit(l);
)
if((portnumber=atoi(argv[l]))<0){
n1
fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);
exit(l);
LINUX網(wǎng)輅編程
/*服務(wù)器端開始建立socket描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
(
fprintf(stderr,nSocketerror:%s\n\a,\strerror(errno));
exit(l);
)
/*服務(wù)器端填充sockaddr結(jié)構(gòu)*/
bzero(&server_addr,sizeof(structsockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
LINUX網(wǎng)輅編程
/*捆綁sockfd描述符*/
if(bind(sockfd,(structsockaddr*)(&server_addr),
sizeof(structsockaddr))==-1){
fprintf(stderr,nBinderror:%s\n\a*\strerror(errno));
exit(l);
)
/*監(jiān)聽sockfd描述符*/
if(listen(sockfd,5)==-1)
Hn
fprintf(stderr,Listenerror:%s\n\a9strerror(errno));
LINUX網(wǎng)輅編程
exit(l);
)
\¥11陽1){
/*服務(wù)器阻塞,直到客戶程序建立連接*/
sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),
&sin_size))==-1)
Hn
fprintf(stderr,Accepterror:%s\n\a9strerror(errno));
exit(l);
LINUX網(wǎng)輅編程
fprintf(stderr,HServergetconnectionfrom%s\nH,
inet_ntoa(client_addr.sin_addr));
if(write(new_fd,hello,strlen(hello))==-l){
Mn
fprintf(stderr,WriteError:%s\n9strerror(errno));
exit(l);
)
/*這個通信已經(jīng)結(jié)束*/
close(new_fd);
/*循環(huán)下一^個*/
LINUX網(wǎng)輅編程
close(sockfd);
exit(O);
)
客戶端(client-basic?c):
#include<stdio.h>
#include<netdb.h>
#include<sys/socket.h>
#include<sys/errno.h>
intmain(intargc,char*argv口)
LINUX網(wǎng)輅編程
mtsockfd;
charbuffer[1024];
structsockaddr_inserver_addr;
structhostent*host;
intportnumber,nbytes;
if(argc!=3){
n1
fprintf(stderr9Usage:%shostnameportnumber\a\n*,
argv[0]);
exit(l);
LINUX網(wǎng)輅編程
if((host=gethostbyname(argv[l]))==NULL){
fprintf(stderr,nGethostnameerror\nn);
exit(l);
)
if((portnumber=atoi(argv[2]))<0){
fprintf(stderr,HUsage:%shostnameportnumber\a\n1*,
argv[0]);
exit(l);
)
LINUX網(wǎng)輅編程
/*客戶程序開始建立sockfd描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-l){
M
fprintf(stderr9^SocketError:%s\a\n,strerror(errno));
exit(l);
)
/*客戶程序填充服務(wù)端的資料*/
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((structin_addr*)host->h_addr);
LINUX網(wǎng)輅編程
/*客戶程序發(fā)起連接請求*/
if(connect(sockfd,(structsockaddr*)(&server_addr),
sizeof(structsockaddr))==-1){
fprintf(stderr,nConnectError:%s\a\nf\strerror(errno));
exit(l);
)
/*連接成功*/
if((nbytes=read(sockfd,buffer,1024))==-1){
fprintf(stderr,nReadError:%s\nf\strerror(errno));
exit(l);
LINUX網(wǎng)輅編程
buffer[nbytes]=H\On;
printf(MIhavereceived:%s\nf\buffer);
/*結(jié)束通信*/
close(sockfd);
exit(O);
LINUX網(wǎng)輅編程
1.5服務(wù)器和客戶機的信息函數(shù)
1.5.1字節(jié)轉(zhuǎn)換函數(shù)
在網(wǎng)絡(luò)上面有著許多類型的機器,這些機器在表示數(shù)
據(jù)的字節(jié)順序是不同的,比如i386芯片是低字節(jié)在內(nèi)存
地址的低端,高字節(jié)在高端,而alpha芯片卻相反.為了
統(tǒng)一起來在Linux下面,有專門的字節(jié)轉(zhuǎn)換函數(shù).
LINUX網(wǎng)輅編程
unsignedlongmthtonl(unsignedlongmthostlong)
unsignedshortinthtons(unisgnedshortinthostshort)
unsignedlongintntohl(unsignedlongintnetlong)
unsignedshortintntohs(unsignedshortintnetshort)
在這四個轉(zhuǎn)換函數(shù)中,h代表host,n代表network,s
代表short,1代表long,第一個函數(shù)的意義是將本機器
上的long數(shù)據(jù)轉(zhuǎn)化為網(wǎng)絡(luò)上的long.其他幾個函數(shù)的意
義同上.
LINUX網(wǎng)輅編程
1.5.2IP和域名的轉(zhuǎn)換
在網(wǎng)絡(luò)上標(biāo)志一臺機器可以用IP或者是用域名,那
么我們怎么去進(jìn)行轉(zhuǎn)換呢?
structhostent*gethostbyname(constchar*hostname)
structhostent*gethostbyaddr(constchar*addr,intleu,inttype)
structhostent的定義:
LINUX網(wǎng)輅編程
structhostent{
char*h_name;/*主機的正式名稱*/
char*h_aliases;/*主機的別名*/
inth_addrtype;主機的地址類型AF_INET*/
inthJength;/*主機的地址長度對于IP4是4字節(jié)32位*/
char**h_addr_list;/*主機的IP地址列表*/
#defineh_addrh_addr_list[O]/*主機的第一個IP地址*/
)
gethostbyname可以將機器名(如)#
換為一個結(jié)構(gòu)指針.在這個結(jié)構(gòu)里面儲存了域名的信息.
LINUX網(wǎng)輅編程
gethostbyaddr可以將一個32位的IP地址(C0A80001)
轉(zhuǎn)換為結(jié)構(gòu)指針.
這兩個函數(shù)失敗時返回NULL且設(shè)置h_errno錯誤
變量,調(diào)用h_strerror()可以得到詳細(xì)的出錯信息.
1.5.3字符串的IP和32位IP的轉(zhuǎn)換
在網(wǎng)絡(luò)上面我們用的IP都是數(shù)字加點()
構(gòu)成的,而在structin_addr結(jié)構(gòu)中用的是32位的IP,
我們上面那個32位IP(C0A80001)是的,為
了轉(zhuǎn)換我們可以使用下面兩個函數(shù)
LINUX網(wǎng)輅編程
mtmet_aton(constchar*cp,structm_addr*mp)
char^inet_ntoa(structin_addrin)
函數(shù)里面a代表asciin代表network.第一個函數(shù)表
示將a.b.c.d的IP轉(zhuǎn)換為32位的IP,存儲在inp指針里
面.第二個是修32位IP轉(zhuǎn)換為a.b.c.d的格式.
LINUX網(wǎng)輅編程
1.6完整的讀寫函數(shù)
在Linux中把我們前面建立的通道看成是文件描述
符,這樣服務(wù)器端和客戶端進(jìn)行通信時候,只要往文件描
述符里面讀寫東西了.就象我們往文件讀寫一樣.
1.6.1寫函數(shù)write
intwrite(intfd,constvoid*buf,size_tnbytes)
write函數(shù)將buf中的nbytes字節(jié)內(nèi)容寫入文件描述
符fd.成功時返回寫的字節(jié)數(shù).失敗時返回并設(shè)置
errno變量.
LINUX網(wǎng)輅編程
在網(wǎng)絡(luò)程序中,當(dāng)我們向套接字文件描述符寫時有兩
種可能.
(1)write的返回值大于0,表示寫了部分或者是全部的
數(shù)據(jù).
(2)返回的值小于0,此時出現(xiàn)了錯誤.我們要根據(jù)錯誤
類型來處理.
如果錯誤為EINTR表示在寫的時候出現(xiàn)了中斷錯誤.
LINUX網(wǎng)輅編程
如果為EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問題(對方已經(jīng)
關(guān)閉了連接).
1.6.2讀函數(shù)read
intread(intfd,void*buf,size_tnbyte)
read函數(shù)是負(fù)責(zé)從fd中讀取內(nèi)容.當(dāng)讀成功時,read
返回實際所讀的字節(jié)數(shù),如果返回的值是0表示已經(jīng)讀
到文件的結(jié)束了,小于0表示出現(xiàn)了錯誤.如果錯誤為
EINTR說明讀是由中斷引起的,如果是ECONNREST
表示網(wǎng)絡(luò)連接出了問題.
LINUX網(wǎng)輅編程
1.6.3send()與recv()
send和recv函數(shù)提供了和read和write差不多的功
能.
send()調(diào)用用于在參數(shù)s指定的已連接的數(shù)據(jù)報或流
套接字上發(fā)送輸出數(shù)據(jù),格式如下:
intsend(intsockfd,void*buf,intlen,intflags)
參數(shù)sockfd為已連接的本地套接字描述符。buf指
向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針,其長度由len指定。
flags指定傳輸控制方式,如是否發(fā)送帶外數(shù)據(jù)等。
LINUX網(wǎng)輅編程
如果沒有錯誤發(fā)生,send。返回總共發(fā)送的字節(jié)數(shù)。否
則它返回SOCKET_ERROR。
recv()調(diào)用用于在參數(shù)sockfd指定的已連接的數(shù)據(jù)
報或流套接字上接收輸入數(shù)據(jù),格式如下:
intrecv(intsockfd,void*buf,intleu,intflags)
參數(shù)sockfd為已連接的套接字描述符。Buf指向接收
輸入數(shù)據(jù)緩沖區(qū)的指針,其長度由len指定。flags指
定傳輸控制方式,如是否接收帶外數(shù)據(jù)等。
LINUX網(wǎng)輅編程
如果沒有錯誤發(fā)生,recv()返回總共接收的字節(jié)數(shù)。如
果連接被關(guān)閉,返回0。否則它返回
SOCKETERRORo
LINUX網(wǎng)輅編程
1.6.4關(guān)閉套接字——close()
closesocket()關(guān)閉套接字sockfd,并釋放分配給該套
接字的資源;如果sockfd涉及一個打開的TCP連接,
則該連接被釋放。dosesocket。的調(diào)用格式如下:
intclose(intsockfd)
參數(shù)sockfd為待關(guān)閉的套接字描述符。如果沒有錯
誤發(fā)生,closesocket()返回0。否則返回值
SOCKETERRORo
LINUX網(wǎng)輅編程
1.7完整的數(shù)據(jù)收發(fā)與套接字關(guān)閉程序
服務(wù)器端(server-standard.c):
#include<stdio.h>
#include<errno.h>
#include<netdb.h>
#include<sys/socket.h>
intmain(intargc,char*argv[])
intsockfd,new_fd;
structsockaddr_inserver_addr;
structsockaddr_inclient_addr;
LINUX網(wǎng)輅編程
intsin_size,portnumber;
charhello[]=HHello!AreYouFine?\nM;
if(argc!=2)
(
H1
fprintf(stderr9Usage:%sportnumber\a\n*,argv[0]);
exit(l);
)
if((portnumber=atoi(argv[l]))<0)
(
fprintf(stderr,MUsage:%sportnumber\a\n1,argv[0]);
exit(l);
)
LINUX網(wǎng)輅編程
/*服務(wù)器端開始建立socket描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
(
fprintf(stderr,nSocketerror:%s\n\a,\strerror(errno));
exit(l);
)
/*服務(wù)器端填充sockaddr結(jié)構(gòu)*/
bzero(&server_addr,sizeof(structsockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
LINUX網(wǎng)輅編程
/*捆綁sockfd描述符*/
if(bind(sockfd,(structsockaddr*)(&server_addr)9sizeof(struct
sockaddr))==-1)
(
Kn
fprintf(stderr,Bmderror:%s\n\a9strerror(errno));
exit(l);
)
/*監(jiān)聽sockfd描述符*/
if(listen(sockfd,5)==-1){
fprintf(stderr,^Listenerror:%s\n\a,\strerror(errno));
LINUX網(wǎng)輅編程
exit(l);
)
\¥11陽1){
/*服務(wù)器阻塞,直到客戶程序建立連接*/
sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),
&sin_size))==-1)
Hn
fprintf(stderr,Accepterror:%s\n\a9strerror(errno));
exit(l);
LINUX網(wǎng)輅編程
fprintf(stderr,HServergetconnectionfrom
inet_ntoa(client_addr.sin_addr));
if(write(new_fd,hello,strlen(hello))==-l){
1n
fprintf(stderr,,WriteError:%s\n9strerror(errno));
exit(l);
)
while(l)
intflag;
LINUX網(wǎng)輅編程
if((flag=read(new_fd,buf,1024))<0)
(
printf(nReadingdataerror!\nn);
break;
)
if(flag==0)
(
printf(MEndingcurrentconnection!\nH);
break;
)
else
LINUX網(wǎng)輅編程
prmtf(—>%s\n\buf);
if(strstr(buf,nexitn)){
printf(HEndingcurrentconnection!\nM);
break;
)
//if(strstr(buf,nbyen))
//{
//close(new_fd);
//close(sockfd);
//exit(O);
//)
LINUX網(wǎng)輅編程
}//else
}//while
close(new_fd);
}//while
close(sockfd);
exit(O);
LINUX網(wǎng)輅編程
客戶端(client.standard.c):
#include<stdio.h>
#include<netdb.h>
#include<sys/socket.h>
#include<sys/errno.h>
intmain(intargc,char*argv[]){
LINUX網(wǎng)輅編程
mtsockfd;
charbuffer[1024];
structsockaddr_inserver_addr;
structhostent*host;
intportnumber,nbytes;
if(argc!=3)
(
fprintf(stderr,HUsage:%shostnameportnumber\a\n1,
argv[0]);
exit(l);
LINUX網(wǎng)輅編程
if((host=gethostbyname(argv[l]))==NULL)
(
fprintf(stderr,"Gethostnameerror\nM);
exit(l);
)
if((portnumber=atoi(argv[2]))<0)
(
n1
fprintf(stderr9Usage:%shostnameportnumber\a\n*,
argv[0]);
exit(l);
LINUX網(wǎng)輅編程
/*客戶程序開始建立sockfd描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
(
nf
fprintf(stderr9SocketError:%s\a\n\strerror(errno));
exit(l);
)
/*客戶程序填充服務(wù)端的資料*/
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((structin_addr*)host->h_addr);
LINUX網(wǎng)輅編程
/*客戶程序發(fā)起連接請求*/
if(connect(sockfd,(structsockaddr*)(&server_addr),
sizeof(structsockaddr))==-1)
(
nn
fprintf(stderr,ConnectError:%s\a\n9strerror(errno));
exit(l);
)
/*連接成功了*/
if((nbytes=read(sockfd,buffer,1024))==-1)
LINUX網(wǎng)輅編程
fprintf(stderr,HReadError:%s\n,\strerror(errno));
exit(l);
)
buffer[nbytes]=**\0n;
Mn
printf(Ihavereceived:%s\n9buffer);
while(l)
gets(buffer);
if(wri陽sockfd,buffer,sizeof(buffer))<0)
printf(Msendingdataerror!\nn);
LINUX網(wǎng)輅編程
if(strstr(buffer,exitn))
break;
)
/*結(jié)束通信*/
close(sockfd);
exit(O);
LINUX網(wǎng)輅編程
1.8完整的多進(jìn)程套接字通信程序
月艮務(wù)器端(server-advance.c):
#include<stdio.h>
#include<errno.h>
#include<netdb.h>
#include<sys/socket.h>
//maybeusebygetpid()andgetppid()
#include<unistd.h>
intmain(intargc,char*argv[])
LINUX網(wǎng)輅編程
intsockfd,new_fd;
structsockaddr_inserver_addr;
structsockaddr_inclient_addr;
intsin_size,portnumber;
charhello[]=nHello!AreYouFine?'';
charbuf[1024];
intflag;
if(argc!=2)
M11
fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);
exit(l);
LINUX網(wǎng)輅編程
if((portnumber=atoi(argv[l]))<0)
(
fprintf(stderr,nUsage:%sportnumber\a\n1*,argv[O]);
exit(l);
)
/*服務(wù)器端開始建立socket描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
nM
fprintf(stderr,Socketerror:%s\n\a9strerror(errno));
exit(l);
LINUX網(wǎng)輅編程
/*服務(wù)器立而填充sockaddr結(jié)構(gòu)*/
bzero(&server_addr,sizeof(structsockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
/*捆綁sockfd描述符*/
if(bind(sockfd,(structsockaddr*)(&server_addr),sizeof(struct
sockaddr))=="l)
(
fprintf(stderr,HBmderror:%s\n\af\strerror(errno));
exit(l);
)
LINUX網(wǎng)輅編程
/*監(jiān)聽sockfd描述符*/
if(listen(sockfd95)==-1)
(
fprintf(stderr,HListenerror:%s\n\af\strerror(errno));
exit(l);
)
//printfmyPIDnum
printf(nStartingServeratport:%dSuccess.MyPIDis:%d\n
AcceptingRequest…\n'',portnumber,getpid());
while(l)
/*服務(wù)器阻塞,直到客戶程序建立連接*/
LINUX網(wǎng)輅編程
sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),
&sin_size))=="l)
(
fprintf(stderr,nAc
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025屆寧夏吳忠市高三上學(xué)期適應(yīng)性考試(一模)歷史試題(解析版)
- 《社區(qū)自治》課件
- 單位管理制度集合大全職員管理篇
- 單位管理制度匯編大全【人力資源管理】
- 單位管理制度合并選集人事管理
- 單位管理制度分享合集【人事管理】十篇
- 單位管理制度范例匯編【人力資源管理篇】十篇
- 單位管理制度呈現(xiàn)大全【人力資源管理篇】十篇
- 七年級英語Whatwouldyouliketohave課件
- 《珠海酒店信息》課件
- 廣東省中醫(yī)院進(jìn)修申請表
- 大班唱歌《吹泡泡》課件
- 護理人文關(guān)懷示范病房創(chuàng)建及成效14-44-16
- DB37∕T 5112-2018 村莊道路建設(shè)規(guī)范
- 牽引供電系統(tǒng)遠(yuǎn)動技術(shù)概述講解課件
- 義務(wù)教育《道德與法治》課程標(biāo)準(zhǔn)(2022年版)
- 乙肝五項詳解(課堂PPT)
- TD汽車維修公司管理制度和崗位職責(zé)匯編-30頁
- 數(shù)字化設(shè)計與制造PPT課件
- 個人信息查詢使用授權(quán)書
- 工作證明(通用)
評論
0/150
提交評論