網(wǎng)絡(luò)編程實(shí)用教程第二版課件_第1頁
網(wǎng)絡(luò)編程實(shí)用教程第二版課件_第2頁
網(wǎng)絡(luò)編程實(shí)用教程第二版課件_第3頁
網(wǎng)絡(luò)編程實(shí)用教程第二版課件_第4頁
網(wǎng)絡(luò)編程實(shí)用教程第二版課件_第5頁
已閱讀5頁,還剩95頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

網(wǎng)絡(luò)編程實(shí)用教程第二版12.1.1

問題的提出站在應(yīng)用程序?qū)崿F(xiàn)的角度,應(yīng)用程序如何方便地使用協(xié)議棧軟件進(jìn)行通信呢?如果能在應(yīng)用程序與協(xié)議棧軟件之間提供一個(gè)軟件接口,就可以方便客戶與服務(wù)器軟件的編程。22.1 UNIX套接字網(wǎng)絡(luò)編程接口的產(chǎn)生與發(fā)展套接字應(yīng)用程序編程接口是網(wǎng)絡(luò)應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧進(jìn)行通信時(shí)所使用的接口,即應(yīng)用程序與協(xié)議棧軟件之間的接口,簡(jiǎn)稱套接字編程接口(Socket

API)。它定義了應(yīng)用程序與協(xié)議棧軟件進(jìn)行交互時(shí)可以使用的一組操作,決定了應(yīng)用程序使用協(xié)議棧的方式、應(yīng)用程序所能實(shí)現(xiàn)的功能、以及開發(fā)具有這些功能的程序的難度。3加州大學(xué)伯克利(Berkley)分校開發(fā)并推廣了一個(gè)包括

TCP/IP

互聯(lián)協(xié)議的

UNIX

,UNIX

Berkeley

Software

DistributionUNIX)操作系統(tǒng),套接字編程接口是這個(gè)操作系統(tǒng)的一個(gè)部分。后來的許多操作系統(tǒng)并沒有另外搞一套其它的編程接口,而是選擇了對(duì)于套接字編程接口的支持。由于這個(gè)套接字規(guī)范最 是由Berkeley

大學(xué)開發(fā)的,一般將它稱為Berkeley

Sockets規(guī)范。42.1.2

套接字編程接口的起源與應(yīng)用2.1.3

套接字編程接口在Windows和Linux操作系統(tǒng)中得到繼承和發(fā)展微軟公司以UNIX操作系統(tǒng)的BerkeleySockets規(guī)范為范例,定義了WindowsSocktes規(guī)范,全面繼承了套接字網(wǎng)絡(luò)編程接口。詳細(xì)內(nèi)容將在第三章介紹。Linux操作系統(tǒng)中的套接字網(wǎng)絡(luò)編程接口幾乎

與UNIX操作系統(tǒng)的套接字網(wǎng)絡(luò)編程接口一樣。本章著重介紹三大操作系統(tǒng)的套接字網(wǎng)絡(luò)編

程接口的共性問題。5要想實(shí)現(xiàn)套接字編程接口,可以采用兩種實(shí)現(xiàn)方式,一種是在操作系統(tǒng)的內(nèi)核中增加相應(yīng)的軟件來實(shí)現(xiàn),一種是通過開發(fā)操作系統(tǒng)之外的函數(shù)庫來實(shí)現(xiàn)。62.1.4

套接字編程接口的兩種實(shí)現(xiàn)方式2.1.5

套接字通信與UNIX操作系統(tǒng)的輸入/輸出UNIX操作系統(tǒng)對(duì)文件和所有其它的輸入/輸出設(shè)備采用一種統(tǒng)一的的操作模式,就是

“打開-讀-寫-關(guān)閉”(open-read-write-close)的I/O模式。當(dāng)TCP/IP協(xié)議被集成到UNIX內(nèi)核中的時(shí)候,相當(dāng)于在UNIX系統(tǒng)中引入了一種新型的I/O操作,就是應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧來交換數(shù)據(jù)。7在UNIX

系統(tǒng)的實(shí)現(xiàn)中,套接字是完全與其他I/O集成在一起的。操作系統(tǒng)和應(yīng)用程序都將套接字編程接口也看作一種輸入/輸出機(jī)制。但是,用戶進(jìn)程與網(wǎng)絡(luò)協(xié)議的交互作用實(shí)際要比用戶進(jìn)程與傳統(tǒng)的I/O設(shè)備相互作用要復(fù)雜得多。8其次,使用套接字的應(yīng)用程序必須說明許多細(xì)節(jié)。僅僅提供open、read、write、close四個(gè)過程遠(yuǎn)遠(yuǎn)不夠。為避免單個(gè)套接字函數(shù)參數(shù)過多,套接字編程接口的設(shè)計(jì)者定義了多個(gè)函數(shù)。92.2

套接字編程的基本概念圖2.1

電插座與電話插座的作用套接口是對(duì)網(wǎng)絡(luò)中不同主機(jī)上應(yīng)用進(jìn)程之間進(jìn)行雙向通信的端點(diǎn)的抽象,一個(gè)套接口就是網(wǎng)絡(luò)上進(jìn)程通信的一端,提供了應(yīng)用層進(jìn)程利用網(wǎng)絡(luò)協(xié)議棧交換數(shù)據(jù)的機(jī)制。102.2.1

什么是套接字(SOCKET)

11

我們應(yīng)當(dāng)從多個(gè)層面來理解套接字這個(gè)概念的內(nèi)涵。從套接字所處的地位來講,套接字上聯(lián)應(yīng)用進(jìn)程,下聯(lián)網(wǎng)絡(luò)協(xié)議棧,是應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧進(jìn)行通信的接口,是應(yīng)用程序與網(wǎng)絡(luò)協(xié)議棧進(jìn)行交互的接口。圖2.212應(yīng)用進(jìn)程、套接口、網(wǎng)絡(luò)協(xié)議棧及操作系統(tǒng)的關(guān)系從實(shí)現(xiàn)的角度來講,非常復(fù)雜。套接字是一個(gè)復(fù)雜的軟件機(jī)構(gòu),包含了一定的數(shù)據(jù)結(jié)構(gòu),包含許多選項(xiàng),由操作系統(tǒng)內(nèi)核管理。從使用的角度來講,非常簡(jiǎn)單。對(duì)于套接字的操作形成了一種網(wǎng)絡(luò)應(yīng)用程序的編程接口(API)。本書把這一套操作套接字的編程接口函數(shù)稱作套接字編程接口,套接字是它的操作對(duì)象??傊捉幼质蔷W(wǎng)絡(luò)通信的基石。132.2.2

套接字的特點(diǎn)1.通信域接字存在于通信域中,通信域是為了處理一14般的進(jìn)程通過套接字通信而引入的一種抽象概念,套接字通常只和同一域中的套接字交換數(shù)據(jù)。

如果數(shù)據(jù)交換要穿越域的邊界,就一定要執(zhí)行某種解釋程序?,F(xiàn)在,僅僅針對(duì)

Internet

域,并且使用Internet協(xié)議族(即TCP/IP協(xié)議族)來通信。2.套接字具有三種類型每一個(gè)正被使用的套接字都有它確定的類型,只有相同類型的套接字才能相互通信。(

1

)數(shù)據(jù)報(bào)套接字(

DatagraSOCKET)數(shù)據(jù)報(bào)套接字提供無連接的不保證可靠的獨(dú)立的數(shù)據(jù)報(bào)傳輸服務(wù)。在Internet通信域中,數(shù)據(jù)報(bào)套接字使用UDP數(shù)據(jù)報(bào)協(xié)議形成的進(jìn)程間通路,具有UDP協(xié)議為上層所提供的服務(wù)的所有特點(diǎn)。15圖2.316在Internet通信域中,數(shù)據(jù)報(bào)套接字基于UDP協(xié)議(2)流式套接字(Stream

SOCKET)17流式套接字提供雙向的、有序的、無重復(fù)的、無記錄邊界的可靠的數(shù)據(jù)流傳輸服務(wù)。在

Internet通信域中,流式套接字使用TCP協(xié)議形成的進(jìn)程間通路,具有TCP協(xié)議為上層所提供的服務(wù)的所有特點(diǎn),在使用流式套接字傳輸數(shù)據(jù)之前,必須在數(shù)據(jù)的發(fā)送端和接收端之間建立連接,如圖2.4所示。圖2.418在Internet通信域中,流式套接字基于TCP協(xié)議(3)原始式套接字(RAW

SOCKET)19

原始式套接字允許對(duì)較低層次的協(xié)議,如IP、ICMP直接訪問,用于檢驗(yàn)新的協(xié)議的實(shí)現(xiàn)。3.套接字由應(yīng)用層的通信進(jìn)程創(chuàng)建,并為其服務(wù)20就是說,每一個(gè)套接字都有一個(gè)相關(guān)的應(yīng)用進(jìn)程,操作該套接字的代碼是該進(jìn)程的組成部分。4.使用確定的IP地址和傳輸層端口號(hào)21

往往在生成套接字的描述符后,要將套接字與計(jì)算機(jī)上的特定的IP地址和傳輸層端口號(hào)相關(guān)聯(lián),這個(gè)過程稱為綁定。一個(gè)套接口要使用一個(gè)確定的三元組網(wǎng)絡(luò)地址信息,才能使它在網(wǎng)絡(luò)中唯一地被標(biāo)識(shí)。不管是采用對(duì)等模式或者客戶機(jī)/服務(wù)器模式,通信雙方的應(yīng)用程序都需要開發(fā)。雙方所交換數(shù)據(jù)的結(jié)構(gòu)和交換數(shù)據(jù)的順序有特定的要求,不符合現(xiàn)在成熟的應(yīng)用層協(xié)議,甚至需要自己去開發(fā)應(yīng)用層協(xié)議,自己設(shè)計(jì)最適合的數(shù)據(jù)結(jié)構(gòu)和信息交換規(guī)程。222.2.3

套接字的應(yīng)用場(chǎng)合2.2.4

套接字使用的數(shù)據(jù)類型和相關(guān)的問題231.三種表示套接字地址的結(jié)構(gòu)在套接字編程接口中,專門定義了三種結(jié)構(gòu)型的數(shù)據(jù)類型,用來存儲(chǔ)協(xié)議相關(guān)的網(wǎng)絡(luò)地址,在套接字編程接口的函數(shù)調(diào)用中要用到它們。(1)sockaddr結(jié)構(gòu),針對(duì)各種通信域的套接字,存儲(chǔ)它們的地址信息。struct

sockaddr

{unsigned

short

sa_family;

//地址家族char

sa_data;

//協(xié)議地址}24(2)sockaddr_in結(jié)構(gòu),專門針對(duì)Internet通信域,存儲(chǔ)套接字相關(guān)的網(wǎng)絡(luò)地址信息,例如IP地址,傳輸層端口號(hào)等信息。struct

sockaddr_in

{shortint

sin_family;

//地址家族unsigned

short

int

sin_port;

//

端口號(hào)25sin_addr;

//IP

地址sin_zero[8];

//全為0struct

in_addrunsigned

char}(3)in_addr結(jié)構(gòu),專門用來存儲(chǔ)IP地址。

Struct

in_addr{Unsigned

long

s_addrl;}26(4)這些數(shù)據(jù)結(jié)構(gòu)的一般用法:27

首先,定義一個(gè)Sockaddr_in的結(jié)構(gòu)實(shí)例,并將它清零。比如:struct

sockaddr_in

myad;memset(&myad,0,sizeof(structsockaddr_in));然后,為這個(gè)結(jié)構(gòu)賦值,比如:myad.sin_family=AF_INET;myad.sin_port=htons(8080);myad.sin_addr.s_addr=htonl(INADDR-ANY);28第三步:在函數(shù)調(diào)用中使用時(shí),將這個(gè)結(jié)構(gòu)強(qiáng)制轉(zhuǎn)換為sockaddr類型。如:

accept(listenfd,(sockaddr*)(&myad),&addrlen);292.本機(jī)字節(jié)順序和網(wǎng)絡(luò)字節(jié)順序30在具體計(jì)算機(jī)中的多字節(jié)數(shù)據(jù)的存儲(chǔ)順序,稱為本機(jī)字節(jié)順序。多字節(jié)數(shù)據(jù)在網(wǎng)絡(luò)協(xié)議報(bào)頭中的存儲(chǔ)順序,稱為網(wǎng)絡(luò)字節(jié)順序。網(wǎng)絡(luò)應(yīng)用程序要在不同的計(jì)算機(jī)中運(yùn)行,本機(jī)字節(jié)順序是不同的,但是,網(wǎng)絡(luò)字節(jié)順序是一定的。所以,應(yīng)用程序在編程的時(shí)候,在把

IP地址和端口號(hào)裝入套接字的時(shí)候,應(yīng)當(dāng)把它們從本機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序;相反,在本機(jī)輸出時(shí),應(yīng)將它們從網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為本機(jī)字節(jié)順序。31套接字編程接口特為解決這個(gè)問題設(shè)置了四個(gè)函數(shù):htons()

短整數(shù)本機(jī)順序轉(zhuǎn)換為網(wǎng)絡(luò)順序,用于端口號(hào)。htonl()

長(zhǎng)整數(shù)本機(jī)順序轉(zhuǎn)換為網(wǎng)絡(luò)順序,用于IP地址。32ntohs()

短整數(shù)網(wǎng)絡(luò)順序轉(zhuǎn)換為本機(jī)順序,用于端口號(hào)。ntohl()

長(zhǎng)整數(shù)網(wǎng)絡(luò)順序轉(zhuǎn)化為本機(jī)順序,用于IP地址。

這四個(gè)函數(shù)將被轉(zhuǎn)換的數(shù)值作為函數(shù)的參數(shù),函數(shù)返回值是轉(zhuǎn)換后的結(jié)果。333.點(diǎn)分十進(jìn)制的IP地址的轉(zhuǎn)換34在因特網(wǎng)中,IP

地址常常用點(diǎn)分十進(jìn)制的表示方法,但在套接字中,IP地址是無符號(hào)的長(zhǎng)整型數(shù),套接字編程接口設(shè)置了兩個(gè)函數(shù),專門用于兩種形式的IP地址的轉(zhuǎn)換。(1)inet-addr函數(shù)35unsigned

long

inet-addr(

const

char*cp)入口參數(shù)cp:點(diǎn)分十進(jìn)制形式的IP地址。返回值:網(wǎng)絡(luò)字節(jié)順序的IP地址,是無符號(hào)的長(zhǎng)整數(shù),(2)inet_ntoa函數(shù)36char*

inet_ntoa(struct

in_addr

in)入口參數(shù)in:包含長(zhǎng)整型IP地址的in_addr

結(jié)構(gòu)變量,返回值:指向點(diǎn)分十進(jìn)制IP地址的字符串的指針。通常,我們使用域名來標(biāo)識(shí)站點(diǎn),可以將文字型的主機(jī)域名直接轉(zhuǎn)換成IP地址,struct

hostent*

gethodtbyname(

constchar*

name);入口參數(shù):是站點(diǎn)的主機(jī)域名字符串,返回值: 是指向hostent

結(jié)構(gòu)的指針,hostent結(jié)構(gòu)包含主機(jī)名,主機(jī)別名數(shù)組,返回地址的類型(一般是AF-INET),地址長(zhǎng)度的字節(jié)數(shù),已符合網(wǎng)絡(luò)字節(jié)順序的主機(jī)網(wǎng)絡(luò)地址等。374.域名服務(wù)2.3

面向連接的套接字編程382.3.1

套接字的工作過程2.3.2 UNIX套接字編程接口的系統(tǒng)調(diào)用391.創(chuàng)建套接字SOCKET()SOCKET過程創(chuàng)建一個(gè)套接字并返回一個(gè)整型描述符:int SOCKET(

int

Protofamily, int

Type,int

Protocol);2.綁定套接字到指定的地址BIND()40int

BIND(

int

Sockfd,

struct

sockaddr*My_addr,

int

Addrlen);3.啟動(dòng)監(jiān)聽Listen()41int

LISTEN(

int

Sockfd,

int

Queuesize);舉例:LISTEN(Sockfe,10);圖2.642監(jiān)聽套接字使用緩沖區(qū)接納多個(gè)客戶端的連接請(qǐng)求4.接收連接請(qǐng)求ACCEPT()int

ACCEPT(int

Sockfd,

struct

sockaddr*

Addr,int*

addrlen);舉例:int

clientfd;

//定義響應(yīng)套接字描述//獲得套接字//定義用于返符變量int

addrler=sizeof(sockaddr);地址結(jié)構(gòu)長(zhǎng)度。struct

sockaddr_in

cltsockaddr;回客戶端地址的結(jié)構(gòu)。clientfd=ACCEPT(listenfd,

(sockaddr*)(&cltsockaddr),&addrlen);

//接收客戶連接請(qǐng)求435.請(qǐng)求建立連接CONNECT()44int

CONNECT(

int

Sockfd,

struct

sockaddr*Service_addr,

int

Addrlen);舉例:(structif

(CONNECT(sockfd,sockaddr*)(&serv_addr),sizeof(structsockaddr))<0){報(bào)錯(cuò),并退出}6.讀/寫套接字READ()和WRITE()45int

READ(

int

sockfd,

void*

buffer,

int

len

);int

WRITE(

int

sockfd,

void*

buffer,

int

len

)向套接字發(fā)送SEND()和從套接字接收RECV()int

SEND(

int

sockfd,

char*

buf,

intlen,int

flags

);int

RECV(

int

sockfd,

char*

buf,

intlen,int

flags

);468.關(guān)閉套接字CLOSE()47int

CLOSE(

int

sockfd

);2.3.3

面向連接的套接字編程實(shí)例1.實(shí)例的功能服務(wù)器對(duì)來訪的客戶計(jì)數(shù),并向客戶報(bào)告這個(gè)計(jì)數(shù)值??蛻艚⑴c服務(wù)器的一個(gè)連接并等待它的輸出。每當(dāng)連接請(qǐng)求到達(dá)時(shí),服務(wù)器生成一個(gè)可打印的ASCII串信息,將它在連接上發(fā)回,然后關(guān)閉連接??蛻麸@示收到的信息,然后退出。48

49例如,對(duì)于服務(wù)器接收的第10次客戶連接請(qǐng)求,該客戶將收到并打印如下信息:This

server

has

been

contacted10times.2.實(shí)例程序的命令行參數(shù)50實(shí)例是UNIX環(huán)境下的C程序,客戶和服務(wù)器程序在編譯后,均以命令行的方式執(zhí)行。服務(wù)器程序執(zhí)行時(shí)可以帶一個(gè)命令行參數(shù),是用來接受請(qǐng)求的監(jiān)聽套接字的協(xié)議端口號(hào)。這個(gè)參數(shù)是可選的。如果不指定端口號(hào),代碼將使用程序內(nèi)定的缺省端口號(hào)5188??蛻舫绦驁?zhí)行時(shí)可以帶兩個(gè)命令行參數(shù):一個(gè)是服務(wù)器所在計(jì)算機(jī)的主機(jī)名,另一個(gè)是服務(wù)器監(jiān)聽的協(xié)議端口號(hào)。這兩個(gè)參數(shù)都是可選的。如果沒有指定協(xié)議端口號(hào),客戶使用程序內(nèi)定的缺省值5188。如果一個(gè)參數(shù)也沒有,客戶使用缺省端口和主機(jī)名localhost,localhost是映射到客戶所運(yùn)行的計(jì)算機(jī)的一個(gè)別名。允許客戶與本地機(jī)上的服務(wù)器通信,對(duì)調(diào)試是很有用的。513.客戶程序代碼52/*----------------------------------------------------程序:client.c目的:創(chuàng)建一個(gè)套接字,通過網(wǎng)絡(luò)連接一個(gè)服務(wù)器,并打印來自服務(wù)器的信息語法:client[host[port]]**host-運(yùn)行服務(wù)器的計(jì)算機(jī)的名字port-服務(wù)器監(jiān)聽套接字所用協(xié)議端口號(hào)注意:兩個(gè)參數(shù)都是可選的。如果未指定主機(jī)名,客戶使用localhost;如果未指定端口號(hào),客戶將使用PROTOPORT中給定的缺省協(xié)議端口號(hào)*----------------------------------------------------/*

UNIX下,套接字的相關(guān)包含文件。*/53#include

<sys/types.h>#include

<sys/socket.h>#include

<netinet/in.h>#include

<arpa/inet.h>#include

<netdb.h>#include

<stdio.h>#include

<string.h>#define

PROTOPORT

5188 /*缺省協(xié)議端口號(hào)*/extern

int

errno;char

localhost=“l(fā)ocalhost”;

/*缺省主機(jī)名*/main(argc,argv)int

argc;

char

*argv[];{struct

hostent

*ptrh;54/*

指向主機(jī)列表中一個(gè)條目的指針*/struct

sockaddr_in

servaddr; /*

存放服務(wù)器端網(wǎng)絡(luò)地址的結(jié)構(gòu)*/intintsockfd;port;char*

host;int

n;char buf[1000]

;/*

客戶端的套接字描述符*//*

服務(wù)器端套接字協(xié)議端口號(hào)*//*

服務(wù)器主機(jī)名指針*//*

讀取的字符數(shù)*//*

緩沖區(qū),接收服務(wù)器發(fā)來的數(shù)據(jù)*/memset((char*)&

servaddr,0,sizeof(servaddr));

/*清空sockaddr結(jié)構(gòu)*/servaddr.sin_family=AF_INET; /*

設(shè)置為因特網(wǎng)協(xié)議族*//*

檢查命令行參數(shù),如果有,就抽取端口號(hào)。否則使用內(nèi)定的缺省值*/if(argc>2){/*

如果指定了協(xié)議端口,就轉(zhuǎn)/*

否則,使用缺省端口號(hào)*//*

如果端口號(hào)是合法的數(shù)值,就將它port

=atoi(argv[2]);換成整數(shù)*/}else

{port

=PROTOPORT;}if(port>0)裝入網(wǎng)絡(luò)地址結(jié)構(gòu)*/55servaddr.sin_port

=

htons((u_short)port);else{ /*

否則,打印錯(cuò)誤信息并退出*/fprintf(stderr,”bad

port

number

%s\n”,argv[2]);exit(1);}/*

檢查主機(jī)參數(shù)并指定主機(jī)名*/if(argc>1){56/*

如果指定了主機(jī)名參數(shù),就使用/*

否則,使用缺省值*/host

=argv[1];它*/}else{host

=localhost;}/*

將主機(jī)名轉(zhuǎn)換成相應(yīng)的IP地址并復(fù)制到servaddr結(jié)構(gòu)中*/57/*

從服務(wù)器主機(jī)名得到/*

檢查主機(jī)名的有效性,ptrh

=

gethostbyname(

host);相應(yīng)的IP地址*/if (

(char

*)ptrh

==

null

)

{無效則退出*/fprintf(

stderr,

”invalid

host:

%s\n”,

host);exit(1);}memcpy(&servaddr.sin_addr,

ptrh->h_addr,

ptrh->h_length

);/*

創(chuàng)建一個(gè)套接字*/sockfd

=

SOCKET(AF_INET,

SOCK_STREAM,

0);if

(sockfd

<

0)

{fprintf(stderr,

”socket

creation

failed\n”

);exit(1);}/*

請(qǐng)求連接到服務(wù)器*/if

(connect(

sockfd,

(struct

sockaddr

*)&

servaddr,sizeof(servaddr))

<

0)

{/*

連接請(qǐng)求被拒絕,fprintf(stderr,”connect

failed\n”);報(bào)錯(cuò)并退出*/exit(1);}58/*

從套接字反復(fù)讀數(shù)據(jù),并輸出到用戶屏幕上*/n=recv(sockfd,buf,sizeof(buf),0);while

(

n

>

0)

{write(1,buf,

n);n

=

recv(

sockfd

,

buf,

sizeof(

buf

),

0

);}/*

關(guān)閉套接字*/closesocket(sockfd);/*

終止客戶程序*/exit(0);}594.服務(wù)器實(shí)例代碼/*程序:server.c目的:分配一個(gè)套接字,然后反復(fù)執(zhí)行如下幾步:(1)等待客戶的下一個(gè)連接(2)發(fā)送一個(gè)短消息給客戶(3)關(guān)閉與客戶的連接(4)轉(zhuǎn)向(1)步命令行語法:server[port]port

–服務(wù)器端監(jiān)聽套接字使用的協(xié)議端口號(hào)注意:端口號(hào)可選。如果未指定端口號(hào),服務(wù)器使用PROTOPORT中指定的缺省端口號(hào)**/60#include

<sys/types.h>#include

<sys/socket.h>/*

監(jiān)聽套接字的缺省/*

監(jiān)聽套接字的請(qǐng)求隊(duì)#include

<netinet/in.h>#include

<netdb.h>#include

<stdio.h>#include

<string.h>#define

PROTOPORT

5188協(xié)議端口號(hào)*/#define

QLEN

6列大小*/int visits

=

0;/*

對(duì)于客戶連接的計(jì)數(shù)*/61main(argc,argc)int

argc;char*

argv[];/*

指向主機(jī)列表中一個(gè)條目{struct

hostent

*ptrh;的指針*/struct

sockaddr_in

servaddr; /*

存放服務(wù)器網(wǎng)絡(luò)地址的結(jié)構(gòu)*/struct

sockaddr_in

clientaddr; /*

存放客戶網(wǎng)絡(luò)地址的結(jié)構(gòu)*//*

監(jiān)聽套接字描述符*//*

響應(yīng)套接字描述符*//*

協(xié)議端口號(hào)*//*

地址長(zhǎng)度*//*

供服務(wù)器發(fā)送字符串所用的int

listenfd;int

clientfd;int

port;

int

alen;char

buf[1000];緩沖區(qū)*/62memset(

(char*)&

servaddr,

0,

sizeof(servaddr)

);

/*清空sockaddr結(jié)構(gòu)*/servaddr.sin_family=AF_INET; /*

設(shè)置為因特網(wǎng)協(xié)議族*/servaddr.sin_addr.s_addr=INADDR_ANY; /*

設(shè)置本地IP地址*//*

檢查命令行參數(shù),如果指定了,就是用該端口號(hào),否則使用缺省端口號(hào)*/if

(argc

>

1){/*

如果指定了端口號(hào),就將它/*

否則,使用缺省端口號(hào)*/port

=atoi(argv[1]);轉(zhuǎn)換成整數(shù)*/}

else

{port

=PROTOPORT;}63if (port

>

0) /*

測(cè)試端口號(hào)是否合法

*/servaddr.sin_port=htons(

(u_short)port

);

else{ /*

打印錯(cuò)誤信息并退出

*/64number

%s\n”,

argv[1]fprintf(

stderr,

”bad

port);exit(1);}/*

創(chuàng)建一個(gè)用于監(jiān)聽的流式套接字*/listenfd

=

SOCKET(AF_INET,SOCK_STREAM,0);if (listenfd

<0)

{fprintf(

stderr,

“socket

creation

failed\n”

);exit(1);}/*

將本地地址綁定到監(jiān)聽套接字*/if

(

bind(

listenfd,

(struct

sockaddr

*)&

servaddr,65sizeof(servaddr))

<

0)

{fprintf(stderr,

”bind

failed\n”

);exit(1);}/*

開始監(jiān)聽,并指定監(jiān)聽套接字請(qǐng)求隊(duì)列的長(zhǎng)度

*/if (listen(listenfd,

QLEN)

<

0)

{fprintf(stderr,

”listen

filed\n”

);exit(1);}/*

服務(wù)器主循環(huán)—接受和處理來自客戶端的連接請(qǐng)求*/while(1){alen=sizeof(clientaddr); /*

接受客戶端連接請(qǐng)求,并生成響應(yīng)套接字*/if((clientfd

=

accept(

listenfd,

(struct

sockaddr

*)&clientaddr,

&alen))

<

0

)

{fprintf(

stderr,

“accept

failed\n”);exit(1);}visits++; /*

累加訪問的客戶數(shù)

*/sprintf(

buf,

“this

server

has

been

contacted

%dtime

\n”,

visits

);send(clientfd,buf,strlen(buf),0); /*

向客戶端發(fā)送信息*//*

關(guān)閉響應(yīng)套接字*/closesocket(

clientfd

);}}66關(guān)于阻塞的問題圖2.767服務(wù)器進(jìn)程因調(diào)用ACCEPT()而被阻塞2.3.4

進(jìn)程的阻塞問題和對(duì)策681.什么是阻塞

阻塞是指一個(gè)進(jìn)程執(zhí)行了一個(gè)函數(shù)或者系統(tǒng)調(diào)用,該函數(shù)由于某種原因不能立即完成,因而不能返回調(diào)用它的進(jìn)程,導(dǎo)致進(jìn)程受控于這個(gè)函數(shù)而處于等待的狀態(tài),進(jìn)程的這種狀態(tài)稱為阻塞。圖2.8

RECV()函數(shù)的兩種執(zhí)行方式692.能引起阻塞的套接字調(diào)用70在Berkeley

套接字網(wǎng)絡(luò)編程接口的模型中,套接字的默認(rèn)行為是阻塞的,具體地說,在一定情況下,有多個(gè)操作套接字的系統(tǒng)調(diào)用會(huì)引起進(jìn)程阻塞。ACCEPT()READ()、RECV()和READFORM()WRITE()、SEND()和SENDTO()CONNECT()SELECT()CLOSESOCKET()3.阻塞工作模式帶來的問題采用阻塞工作模式的單進(jìn)程服務(wù)器是不能很好地同時(shí)為多個(gè)客戶服務(wù)的。圖2.9是一個(gè)例子。圖2.971采用阻塞工作模式的服務(wù)器不能很好地為多個(gè)客戶服務(wù)4.一種解決方案72

利用UNIX操作系統(tǒng)的FORK()系統(tǒng)調(diào)用,編制多進(jìn)程并發(fā)執(zhí)行的服務(wù)器程序??梢詣?chuàng)建子進(jìn)程。對(duì)于每一個(gè)客戶端,用一個(gè)專門的進(jìn)程為它服務(wù),通過進(jìn)程的并發(fā)執(zhí)行,來實(shí)現(xiàn)對(duì)多個(gè)客戶的并發(fā)服務(wù)?;镜木幊炭蚣苁牵焊高M(jìn)程代碼If

((pid

=

FORK())

==

0)

{…….子進(jìn)程代碼........}

else

if

(pid<0)

{報(bào)錯(cuò)信息}父進(jìn)程代碼73舉例:#include

<sys/types.h>#include

<sys/socket.h>#include

<stdio.h>#include

<arpa/inet.h>void

main(int

argc,

char**

argv){int

listenfd,clientfd,pid;struct

sockaddr_in

ssockaddr,

csockaddr;char

buffer[1024];int

addrlen,n;74/*

創(chuàng)建監(jiān)聽套接字*/listenfd

=

socket(AF_INET,SOCK_STREAM,0);if (listenfd

<

0)

{fprintf(stderr,

"socket

error!\n");exit(1);}75/*

為監(jiān)聽套接字綁定網(wǎng)絡(luò)地址*/memset(&ssockaddr,0,sizeof(struct

sockaddr_in));ssockaddr.sin_family=AF_INET;ssockaddr.sin_addr.s_addr=htonl(INADDR_ANY);ssockaddr.sin_port=htons(8080);if

(bind(listenfd,&ssockaddr,sizeof(structsockaddr_in))

<

0)

{fprintf(stderr,

"bind

error!\n");exit(2);}76/*

啟動(dòng)套接字的監(jiān)聽*/listen(listenfd,5);addrlen

=

sizeof(sockaddr);77服務(wù)器進(jìn)入循環(huán),接受并處理來自不同客戶端的連接請(qǐng)求=/**/while

(1)

{clientfdaccept(listenfd,(sockaddr*)(&csockaddr),&addrlen);/*

accept

調(diào)用返回時(shí),表明有客戶端請(qǐng)求連接,創(chuàng)建子進(jìn)程處理連接*/If

((pid

=

FORK())

==

0)

{/*

顯示客戶端的網(wǎng)絡(luò)地址*/printf("ClientAddr:%s%d\n",inet_ntoa(csockaddr.sin_addr),ntohs(csockaddr.sin_port));/*

讀取客戶端發(fā)送來的數(shù)據(jù),在將它們返回到客戶端*/while((n=read(clientfd,buffer,1024))>0){

buffer[n]=0;printf("Client

Send:

%s",buffer);write(

clientfd,

buffer,

n);}if

(n

<

0)

{fprintf(

stderr,

"read

error!\n");exit(3);}78/*

通信完畢,關(guān)閉與這個(gè)客戶連接的套接字*/79closed!\n",printf("clent

%sinet_ntoa(csockaddr.sin_addr));close(clientfd);exit(1);}

else

if

(pid

<

0)

printf("fork

failed!\n");close(clientfd);}close(listenfd); /*

關(guān)閉監(jiān)聽套接字*/}2.4

無連接的套接字編程802.4.1

無連接的套接字編程的兩種模式使用數(shù)據(jù)報(bào)套接字開發(fā)網(wǎng)絡(luò)應(yīng)用程序,既可以采用客戶/服務(wù)器模式,也可以采用對(duì)等模式。圖2.1081對(duì)等模式的數(shù)據(jù)報(bào)套接字的編程模型1.對(duì)等模式2.客戶/服務(wù)器模式圖2.11 C/S模式的數(shù)據(jù)報(bào)套接字的編程模型822.4.2

兩個(gè)專用的系統(tǒng)調(diào)用83發(fā)送數(shù)據(jù)報(bào)SENDTO()int

SENDTO(

int

sockfd,

const

void*

msg,

intlen,

unsigned

int

flags,

struct

sockaddr*

to,int

tolen);接收數(shù)據(jù)報(bào)RECVFROM()int

RECVFROM(

int

sockfd,

void*

buf,

intlen,unsigned

int

flags,

struct

sockaddr*

from,int*

fromlen

)2.4.3

數(shù)據(jù)報(bào)套接字的對(duì)等模式編程實(shí)例聊天程序#include

<sys/types.h>#include

<unistd.h>#include

<error.h>#include

<sys/socket.h>#include

<arpa/inet.h>#include

<stdio.h>/*

中斷處理過程*/void

int_proc(

int

signo)

{

}84void

main(int

argc,

char**

argv){struct

sockaddr_in daddr,

saddr,

cmpaddr;int

sockfd;int

timer

=

3;

char

buffer[1024];int

addrlen,n;/*

判斷用戶輸入的命令行是否正確,如果有錯(cuò),提示用法*/if

(argc!=5){85目的

IP

目的端口 源

IP

源端口

\n",printf("用法:%sargv[0]);exit(0);}/*

設(shè)定中斷處理函數(shù),并設(shè)置時(shí)間限制*/signal(SIGALRM,int_proc);alarm(timer);/*

建立數(shù)據(jù)報(bào)套接字*/sockfd

=

socket(AF_INET,

SOCK_DGRAM,

0);if

(sockfd

<

0)

{fprintf(stderr,

"socket

error!\n");exit(1);}86/*為結(jié)構(gòu)變量daddr的各個(gè)字段賦值*/addrlen=sizeof(struct

sockaddr_in);memset(&daddr,0,addrlen);daddr.sin_family=AF_INET;daddr.sin_port=htons(atoi(argv[2]));if

(inet_pton(AF_INET,

argv[1],

&daddr.sin_addr

)<=

0)

{fprintf(stderr,

"Invaild

dest

IP!\n");exit(0);}87/*

為結(jié)構(gòu)變量saddr的各個(gè)字段賦值*/addrlen=sizeof(struct

sockaddr_in);memset(&saddr,0,addrlen);saddr.sin_family=AF_INET;saddr.sin_port=htons(atoi(argv[4]));if

(inet_pton(AF_INET,

argv[3],

&saddr.sin_addr

)<=

0)

{fprintf(stderr,

"Invaild

source

IP!\n");exit(0);}88/*

綁定地址*/if

(bind(sockfd,

&saddr,

addrlen)

<

0

)

{fprintf(stderr,

"bind

local

addr

error!\n");exit(1);}/*

從標(biāo)準(zhǔn)輸入獲得字符串,并發(fā)送給目標(biāo)地址*/if

(fgets(buffer,

1024,

stdin)

==

NULL

)

exit(0);if

(

sendto(

sockfd,

buffer,

strlen(buffer),

0,&daddr,

addrlen))

{fprintf(stderr,

"sendto

error!\n");exit(2);}89while

(1)

{/*

接收信息并顯示*/n

=

recvfrom(

sockfd,

buffer,

1024,

0,

&cmpaddr,&daddrlen

);if

(n

<

0)

{/*

根據(jù)

errno

中的數(shù)值是否為常

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論