- 論壇徽章:
- 0
|
UNIX系統(tǒng)TCP/IP Socket接口的應(yīng)用
Socket接口是TCP/IP網(wǎng)絡(luò)的API(應(yīng)用程序接口)。它定義了許多函數(shù)和例程,程序員可以用它開(kāi)發(fā)TCP/IP網(wǎng)絡(luò)上的應(yīng)用程序,可以在CLIENT/SERVER模型中制作自己的應(yīng)用程序通信接口。今天,Socket接口是TCP/IP網(wǎng)絡(luò)最為通用的API之一。
TCP/IP協(xié)議組可以實(shí)現(xiàn)面向連接和無(wú)連接兩種網(wǎng)絡(luò)通信。面向連接的協(xié)議在兩個(gè)連接端點(diǎn)之間建立一條虛電路,兩個(gè)端點(diǎn)之間的鏈路可以看作是直接的點(diǎn)到點(diǎn)的連接。兩個(gè)端點(diǎn)只有在建立連接后才能傳輸數(shù)據(jù)。在面向連接的網(wǎng)絡(luò)通信中,數(shù)據(jù)按字節(jié)流形式傳輸。無(wú)連接協(xié)議在傳輸報(bào)文前,不用建立連接。無(wú)連接協(xié)議每個(gè)報(bào)文包含一個(gè)完整,精確的傳送地址。在無(wú)連接網(wǎng)絡(luò)通訊中,數(shù)據(jù)按數(shù)據(jù)包形式流動(dòng)。網(wǎng)絡(luò)通信包含兩臺(tái)主機(jī)或兩個(gè)進(jìn)程,網(wǎng)絡(luò)對(duì)話的每一端稱為一個(gè)端點(diǎn)?蓪ocket看作網(wǎng)絡(luò)通信的一個(gè)端點(diǎn),當(dāng)使用Socket接口對(duì)網(wǎng)絡(luò)通信編程時(shí),Socket是網(wǎng)絡(luò)通信過(guò)程中端點(diǎn)的抽象表示。程序在網(wǎng)絡(luò)對(duì)話的每端都需要先建立一個(gè)Socket,即為一個(gè)Socket數(shù)據(jù)結(jié)構(gòu)分配存儲(chǔ)空間。
圖1,圖2是面向連接的和無(wú)連接的Socket使用綜述圖。
圖1 面向連接協(xié)議時(shí)socket的使用
圖2 無(wú)連接協(xié)議時(shí)socket的使用
一.系統(tǒng)調(diào)用說(shuō)明:??
有關(guān)消息隊(duì)列的系統(tǒng)調(diào)用均需下列頭文件:
#include <sys/types.h>;
#include <sys/socket.h>;
#include <arpa/inet.h>;
#include <netinet/in.h>;
#include <fcntl.h>;
1。socket 調(diào)用
建立一個(gè)socket,即為一個(gè)socket數(shù)據(jù)結(jié)構(gòu)分配存儲(chǔ)空間。并初步定義socket的屬性。調(diào)用格式如下:
int s, domain, type, protocol;
s = socket (domain, type, protocol);
domain:協(xié)議族和地址族,確定socket使用的一組協(xié)議。與三個(gè)符號(hào)常數(shù)有關(guān):
PF_INET 表示互聯(lián)網(wǎng)協(xié)議族,一般選用此參數(shù)。
PF_VNIX 表示VNIX內(nèi)部協(xié)議族。
PF_NS 表示Xerox網(wǎng)絡(luò)服務(wù)協(xié)議族。
type:通信類型,與兩個(gè)符號(hào)常數(shù)有關(guān):
SOCK_STREAM 以字節(jié)流形式通信,面向連接的協(xié)議使用這種形式。
SOCK_DGRAM 數(shù)據(jù)以獨(dú)立的數(shù)據(jù)包形式流動(dòng),無(wú)連接協(xié)議使用這種形式。
protocol:指明此socket請(qǐng)求使用的協(xié)議,可以使用相關(guān)符號(hào)常數(shù)來(lái)表示:
IPPROTO_TCP 表示TCP協(xié)議。
IPPROTO_UDP 表示UDP協(xié)議。
s:如調(diào)用成功,返回socket描述符。否則返回-1。
2。connect調(diào)用
啟動(dòng)和遠(yuǎn)地主機(jī)的直接連接,只有面向連接的客戶程序使用此函數(shù)。調(diào)用格式如下:
int s, namelen;
struct sockaddr *name;
int connect (s, name, namelen);
s: socket句柄,是socket函數(shù)返回的socket描述符。
name: 遠(yuǎn)地socket地址,是一個(gè)指向特定socket地址結(jié)構(gòu)的指針。此結(jié)構(gòu)保存了一個(gè)地址族,一個(gè)協(xié)議端口和一個(gè)網(wǎng)絡(luò)主機(jī)地址。
namelen: 地址長(zhǎng)度,簡(jiǎn)單地告訴socket執(zhí)行體遠(yuǎn)地socket地址結(jié)構(gòu)(第二個(gè)參數(shù))按字節(jié)的大小。
返回值: 連接成功返回0,不成功返回-1。
3。bind調(diào)用
配置一個(gè)socket的本地地址(包括主機(jī)地址和協(xié)議端口)。指明socket將使用本地的哪一個(gè)協(xié)議端口進(jìn)行數(shù)據(jù)傳送。面向連接的服務(wù)器程序,無(wú)連接的服務(wù)器程序,無(wú)連接的客戶程序使用此函數(shù)。調(diào)用格式如下:
int s, namelen;
struct sockaddr *name;
int bind (s, name, namelen);
s: socket句柄,socket函數(shù)返回的socket描述符。
name: 本地socket地址,是指向特定socket地址結(jié)構(gòu)的指針。指明用于通信的本地協(xié)議端口。
namelen: 本地socket地址結(jié)構(gòu)的長(zhǎng)度。
返回值: 調(diào)用成功返回0,不成功返回-1。
4。listen調(diào)用
建立一個(gè)輸入數(shù)據(jù)隊(duì)列,將到達(dá)本地的客戶服務(wù)請(qǐng)求保存在此隊(duì)列上,直到程序處理它們。面向連接的服務(wù)器程序使用此函數(shù)。調(diào)用格式如下:
int s, backlog;
int listen (s, backlog);
s: socket句柄,socket函數(shù)返回的socket描述符。
backlog: 定義最大隊(duì)列長(zhǎng)度,一般可定義為5。如果一個(gè)客戶服務(wù)請(qǐng)求到來(lái)時(shí),輸入隊(duì)列已滿,此socket將拒絕連接請(qǐng)求客戶程序?qū)⑹艿揭粋(gè)出錯(cuò)信息。
返回值: 調(diào)用成功返回0,不成功返回-1。
5。accept調(diào)用
使服務(wù)器進(jìn)程進(jìn)入睡眠狀態(tài),等待客戶進(jìn)程的連接請(qǐng)求。收到連接請(qǐng)求后將建立一個(gè)新的socket,并將新socket和客戶進(jìn)程連接起來(lái)。即將請(qǐng)求連接的客戶進(jìn)程的地址配置到新的socket中去,而初始socket仍具有任意網(wǎng)絡(luò)地址,因此它可繼續(xù)接收進(jìn)來(lái)的服務(wù)請(qǐng)求。以后發(fā)送和接收數(shù)據(jù)都使用這個(gè)新建的socket。面向連接的服務(wù)器進(jìn)程使用此函數(shù)。調(diào)用格式如下:
int s, addrlen;
struct sockaddr *addr;
int accept (s, addr, addrlen);
s: socket句柄,socket函數(shù)返回的socket描述符。在此socket的基礎(chǔ)上建立一個(gè)新socket 。
addr: 用于存放客戶程序socket地址。
addrlen: 客戶程序socket地址長(zhǎng)度。
返回值: 調(diào)用成功返回新建socket的描述符,否則返回-1。
6。write,writev,send,sendto,sendmsg調(diào)用
Socket API提供這五個(gè)函數(shù)來(lái)發(fā)送數(shù)據(jù)。在SCO UNIX環(huán)境中支持其中三個(gè):面向連接的協(xié)議使用write,send兩個(gè)函數(shù)。在面向連接的協(xié)議中,程序使用connect函數(shù)配置一個(gè)面向連接的socket,此socket包含遠(yuǎn)地主機(jī)的地址信息,因此,在傳送數(shù)據(jù)時(shí)不需再指明目的地址。無(wú)連接的協(xié)議使用sendto,函數(shù),在函數(shù)調(diào)用中需指明目的地址。調(diào)用格式如下:
int s, buflen, flags, namelen;
char buf [n];
struct sockaddr *name;
int write (s, buf, buflen);
int send (s, buf, buflen, flags);
int sendto (s, buf, buflen, flags, name, namelen);
s: socket句柄,accept函數(shù)返回的socket描述符。
buf: 指向一個(gè)包含傳輸信息的數(shù)據(jù)緩沖區(qū)。
buflen: 指明傳送數(shù)據(jù)緩沖區(qū)的大小。
flags: 傳輸控制標(biāo)志,可將數(shù)據(jù)設(shè)置為優(yōu)先級(jí)較高的帶外數(shù)據(jù)傳送
name: 遠(yuǎn)地socket地址。
namelen: 遠(yuǎn)地socket地址長(zhǎng)度。
返回值: 調(diào)用成功返回發(fā)送數(shù)據(jù)的長(zhǎng)度(以字節(jié)為單位),否則返回-1。
7.read,readv,reav,recvfrom,recvmsg調(diào)用 socket API提供五個(gè)與傳送數(shù)據(jù)函數(shù)對(duì)應(yīng)的接收函數(shù):read,readv,reav,recvfrom和recvmsg。SCO UNIX環(huán)境中支持其中兩個(gè):面向連接的協(xié)議使用read函數(shù),它與write函數(shù)對(duì)應(yīng)。無(wú)連接的協(xié)議使用recvfrom,它與sendto函數(shù)相對(duì)應(yīng),需在參數(shù)中指明地址。調(diào)用格式如下:
int s, buflen;
char buf [n];
int read (s, buf, buflen);
int recvfrom (s, buf, buflen, flags, name, namelen);
s: socket句柄,accept函數(shù)返回的socket描述符。
buf: 指向一個(gè)包含傳輸信息的數(shù)據(jù)緩沖區(qū)。
buflen: 指明傳送數(shù)據(jù)緩沖區(qū)的大小。
flags: 傳輸控制標(biāo)志,可將數(shù)據(jù)設(shè)置為優(yōu)先級(jí)較高的帶外數(shù)據(jù)傳送。
name: 遠(yuǎn)地socket地址。
namelen: 遠(yuǎn)地socket地址長(zhǎng)度。
返回值: 調(diào)用成功返回接收數(shù)據(jù)的長(zhǎng)度(以字節(jié)為單位),否則返回 -1。
二.示例程序
示例程序可在SCO UNIX環(huán)境下編譯,運(yùn)行,分為服務(wù)器程序和客戶程序,均采用面向連接的協(xié)議?稍诰W(wǎng)絡(luò)或單機(jī)上運(yùn)行(將程序中定義的IP地址根據(jù)情況修改)。與/usr/lib/libsocket.a一起編譯。運(yùn)行時(shí)先啟動(dòng)server進(jìn)程,再啟動(dòng)client進(jìn)程,按提示輸入需傳送的字符,即可看到傳送結(jié)果。
服務(wù)器程序:
#include <fcntl.h>;
#include <netinet/in.h>;
#include <arpa/inet.h>;
#include <sys/types.h>;
#include <sys/socket.h>;
#define SERV_TCP_PORT 6000 //定義服務(wù)器的PORT端口
int sockfd,newsockfd;
err_dump(char *string)
{
printf("%s\n",string);
perror("system error message:" ;
close(sockfd);
close(newsockfd);
}
main()
{
int client,n;
char packet[80],*HELLO="hello",*cli_addr_str;
ushort cli_port;
struct sockaddr_in serv_addr,cli_addr;
if ((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
printf("socket error.\n" ; //建立socket
bzero((char *)&serv_addr,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_TCP_PORT);
//配置地址結(jié)構(gòu)serv_addr
if(bind(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))<0)
//指明socket的數(shù)據(jù)傳送端口地址
{err_dump("Server:bind error." ;exit(1);}
listen(sockfd,5); //建立數(shù)據(jù)輸入隊(duì)列
for (;
{
strcpy(packet,"\0" ;
client=sizeof(cli_addr);
newsockfd=accept(sockfd,(struct sockaddr *)&cli_addr,&client);
//等待客戶進(jìn)程的連接請(qǐng)求
if (newsockfd<0)
{err_dump("Server:accept error." ;exit(1);}
n=read(newsockfd,packet,80); //讀入客戶進(jìn)程傳送的數(shù)據(jù)
packet[n]='\0';
cli_addr_str=inet_ntoa(cli_addr.sin_addr.s_addr);
cli_port=ntohs(cli_addr.sin_port); //轉(zhuǎn)換地址格式,用于顯示
printf("packet from remote ip address:%s.\nremote port:%d.\n",\
cli_addr_str,cli_port);
printf("packet length:%d bytes.\n",n);
printf("packet=%s.\n",packet);
write(newsockfd,HELLO,6); //發(fā)送回應(yīng)信息
close(newsockfd);
}
}
客戶程序:
#include <fcntl.h>;
#include <netinet/in.h>;
#include <sys/types.h>;
#include <sys/socket.h>;
#define HOST_ADDR "134.162.33.50"
#define HOST_PORT 6000
int sockfd;
err_dump(char *string)
{
printf("%s\n",string);
perror("system error message:" ;
close(sockfd);
}
main()
{
char packet[80],bpacket[80],recepack[6],quit[]={'q','\0'};
struct sockaddr_in serv_addr;
int sockfd,connectfd,bindfd,writefd,n;
bzero((char *)&serv_addr,sizeof(serv_addr));
serv_addr.sin_family = PF_INET;
serv_addr.sin_addr.s_addr = inet_addr(HOST_ADDR);
serv_addr.sin_port = htons(HOST_PORT);
for(;
{
printf("input string to send or 'q' to quit.\n" ;
gets(packet);
if (strcmp(packet,quit)==0)
exit(0);
if ((sockfd = socket(PF_INET,SOCK_STREAM,0))<0) //建立socket
{err_dump("socket error." ;exit(1);}
if ((connectfd = connect(sockfd,(struct sockaddr *)&serv_addr,\
sizeof(serv_addr)))<0) //連接服務(wù)器進(jìn)程
{err_dump("connect error.");exit(1);}
if ((writefd=write(sockfd,packet,strlen(packet)))<0)//發(fā)送信息
{err_dump("write error.");exit(1);}
printf ("sockfd=%d,bindfd=%d,connectfd=%d,writefd=%d.\n",\
sockfd,bindfd,connectfd,writefd);
strcpy (recepack,'\0');
read(sockfd,recepack,80); //讀回應(yīng)信息
n = strlen(recepack);
printf("read len=%d.\n",n);
printf("packet from server:%s.\n",recepack);
close(sockfd);
}
} |
|