- 論壇徽章:
- 0
|
Socket
Socket是TCP/IP網(wǎng)絡的API, 可以用它來開發(fā)網(wǎng)絡應用程序
Socket數(shù)據(jù)傳輸是一種特殊的I/O, Socket也是一種文件描述符
.Socket的建立
int socket(int domain, int type, int protocol)
函數(shù)返回:
一個整型的Socket描述符, 可以在后面調(diào)用它
參數(shù)說明:
int domain 指明所使用的協(xié)議族, 通常是PF_INET, 表示網(wǎng)絡(TCP/IP)協(xié)議族
說明我們網(wǎng)絡程序所在的主機采用的通訊協(xié)族(AF_UNIX和AF_INET等).
AF_UNIX : 只能夠用于單一的Unix系統(tǒng)進程間通信,
AF_INET : 是針對Internet的,因而可以允許在遠程主機之間通信
(當我們man socket時發(fā)現(xiàn)domain可選項是 PF_*而不是AF_*,因為glibc是posix的實現(xiàn)所以用PF代替了AF,不過我們都可以使用的)
int type 指定socket的類型, 通常是 SOCK_STREAM 流式Socket這樣會提供按順序的,可靠,雙向,面向連接的比特流和SOCK_DGRAM數(shù)據(jù)報式Socket這樣只會提供定長的,不可靠,無連接的通信
int prottocol 通常為0 由于我們指定了type,所以這個地方我們一般只要用0來代替就可以了
應用示例:
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
.Socket配置
Socket描述符是一個指向內(nèi)部數(shù)據(jù)結構的指針,它指向描述符表入口。調(diào)用Socket函數(shù)時,
socket執(zhí)行體將建立一個Socket,實際上"建立一個Socket"意味著為一個Socket數(shù)據(jù)結構分配存儲空間。
Socket執(zhí)行體為你管理描述符表。
兩個網(wǎng)絡程序之間的一個網(wǎng)絡連接包括五種信息:通信協(xié)議、本地協(xié)議地址、本地主機端口、遠端
主機地址和遠端協(xié)議端口。Socket數(shù)據(jù)結構中包含這五種信息。
通過socket調(diào)用返回一個socket描述符后,在使用socket進行網(wǎng)絡傳輸以前,必須配置該socket:
1)面向連接的socket客戶端通過調(diào)用Connect函數(shù)在socket數(shù)據(jù)結構中保存本地和遠端信息。
2)無連接socket的客戶端和服務端以及面向連接socket的服務端通過調(diào)用bind函數(shù)來配置本地信息。
Bind函數(shù)將socket與本機上的一個端口相關聯(lián),隨后你就可以在該端口監(jiān)聽服務請求。
Bind函數(shù)原型為:
int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
Bind()函數(shù)在成功被調(diào)用時返回0;出現(xiàn)錯誤時返回"-1"并將errno置為相應的錯誤號。
Sockfd 是調(diào)用socket函數(shù)返回的socket描述符,
my_addr 是一個指向包含有本機IP地址及端口號等信息的sockaddr類型的指針;
addrlen 常被設置為sizeof(struct sockaddr)。
1) struct sockaddr結構類型是用來保存socket信息的:
struct sockaddr
{
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 字節(jié)的協(xié)議地址 */
};
sa_family 一般為AF_INET,代表Internet(TCP/IP)地址族;
sa_data 則包含該socket的IP地址和端口號。
2) sockaddr_in結構類型:
struct sockaddr_in
{
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口號 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持與struct sockaddr同樣大小sin_zero用來將sockaddr_in結構填充到與struct sockaddr同樣的長度,可以用bzero()或memset()函數(shù)將其置為零。 */
};
這個結構更方便使用。
指向sockaddr_in 的指針和指向sockaddr的指針可以相互轉換,這意味著如果一個函數(shù)所需參數(shù)類型是sockaddr時,你可以在函數(shù)調(diào)用的時候將一個指向sockaddr_in的指針轉換為指向sockaddr的指針;或者相反。
使用bind函數(shù)時,可以自動獲得本機IP地址和隨機獲取一個沒有被占用的端口號:
/* 系統(tǒng)隨機選擇一個未被使用的端口號;通過將my_addr.sin_port置為0,函數(shù)會自動為你選擇一個未占用的端口來使用 */
my_addr.sin_port = 0;
/* 填入本機IP地址;通過將my_addr.sin_addr.s_addr置為INADDR_ANY,系統(tǒng)會自動填入本機IP地址 */
my_addr.sin_addr.s_addr = INADDR_ANY;
注意在使用bind函數(shù)是需要將sin_port和sin_addr轉換成為網(wǎng)絡字節(jié)優(yōu)先順序;而sin_addr則不需要轉換。
計算機數(shù)據(jù)存儲有兩種字節(jié)優(yōu)先順序:高位字節(jié)優(yōu)先和低位字節(jié)優(yōu)先:
Internet上數(shù)據(jù)以高位字節(jié)優(yōu)先順序在網(wǎng)絡上傳輸,所以對于在內(nèi)部是以低位字節(jié)優(yōu)先方式存儲數(shù)據(jù)的機器,在Internet上傳輸數(shù)據(jù)時就需要進行轉換,否則就會出現(xiàn)數(shù)據(jù)不一致。
下面是幾個字節(jié)順序轉換函數(shù): (h: host n: network l: long s: short)
·htonl():把32位值從主機字節(jié)序轉換成網(wǎng)絡字節(jié)序, 轉為高位字節(jié)優(yōu)先
·htons():把16位值從主機字節(jié)序轉換成網(wǎng)絡字節(jié)序, 轉為高位字節(jié)優(yōu)先
·ntohl():把32位值從網(wǎng)絡字節(jié)序轉換成主機字節(jié)序, 從高位字節(jié)優(yōu)先轉換
·ntohs():把16位值從網(wǎng)絡字節(jié)序轉換成主機字節(jié)序, 從高位字節(jié)優(yōu)先轉換
需要注意的是,在調(diào)用bind函數(shù)時一般不要將端口號置為小于1024的值,因為1到1024是保留端口號,你可以選擇大于1024中的任何一個沒有被占用的端口號。
應用示例:
A)服務端
1)建立結構變量
struct sockaddr_in my_addr;
int SERVPORT;
2)配置協(xié)議族、端口、地址、sin_zero填充位
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SERVPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero), 8);
3)把sockfd的本地端口、IP地址、連接協(xié)議進行綁定
if( bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
{
perror("bind");
return 1;
}
B)客戶端
1)建立結構體變量和端口號
struct sockaddr_in serv_addr;
struct hostent *host;
int SERVPORT;
//struct hostent *host; 把服務器端IP通過gethostbyname賦給host結構體,如果傳入的是域名則轉為IP地址再賦值
if((host = gethostbyname(“www.800hr.com”)) == NULL)
//或 if((host = gethostbyname(“192.168.0.1”)) == NULL)
{
herror("gethostbyname error");
return 1;
}
else
{//輸出IP地址: xxx.xxx.xxx.xxx
printf("host: %s\n", inet_ntoa(*((struct in_addr*)host->h_addr)));
}
2)建立Socket
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("create sock");
return 1;
}
3)給服務端結構變量賦值
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero), 8);
4)連接服務端
//int connect(int socfd, struct sockaddr *serv_addr, int addrlen)
進行客戶端程序設計無須調(diào)用bind(),因為這種情況下只需知道目的機器的IP地址,而客戶通過哪個端口與服務器建立連接并不需要關心,socket執(zhí)行體為你的程序自動選擇一個未被占用的端口,并通知你的程序數(shù)據(jù)什么時候到達端口
Connect函數(shù)啟動和遠端主機的直接連接。只有面向連接的客戶程序使用socket時才需要將此socket與遠端主機相連。
面向連接的服務器從不啟動一個連接,它只是被動的在協(xié)議端口監(jiān)聽客戶的請求。
無連接協(xié)議從不建立直接連接。
if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
{
perror("create sock");
return 1;
}
.Listen()
Listen函數(shù)
1) 使socket處于被動的監(jiān)聽模式
2) 為該socket建立一個輸入數(shù)據(jù)隊列,將到達的服務請求保存在此隊列中,直到程序處理它們
int listen(int socfd, int backlog)
參數(shù)說明:
sockfd 是socket()函數(shù)返回的socket描述符
backlog 指定在請求隊列中允許的最大請求數(shù), 進入的連接請求將在隊列中等待accept它們
如果一個服務請求到來時,輸入隊列己滿, 此socket將拒絕連接請求,客戶收到一個出錯信息
int BACKLOG = 20;
if(listen(sockfd, BACKLOG) == -1)
{
perror("listen");
return 1;
}
.accept()
在建立好輸入隊列后,服務器就調(diào)用accept函數(shù),然后睡眠并等待客戶的連接請求
1) accept() 函數(shù)讓服務器接收客戶的連接請求。
int accept(int sockfd, void *addr, int *addrlen);
參數(shù)說明:
sockfd 被監(jiān)聽的socket描述符
addr sockaddr_in變量的指針, 該變量用來存放請求服務的客戶機的信息
addrlen 通常是sizeof(struct sockaddr_in)
返回值 :
-1 出錯時
client_fd 成功時
[注] 當accept函數(shù)監(jiān)視的socket收到連接請求時,socket執(zhí)行體將建立一個新的socket,執(zhí)行體將這個新socket和請求連接進程的地址聯(lián)系起來,收到服務請求的初始socket仍可以繼續(xù)在以前的 socket上監(jiān)聽,同時可以在新的socket描述符上進行數(shù)據(jù)傳輸操作
int client_fd;
sin_size = sizeof(struct sockaddr_in);
if((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr.sin_addr, &sin_size)) == -1)
{
perror("accept");
continue;
}
.send()
int send(int sockfd, const void *msg, int len, int flags);
參數(shù)列表:
sockfd 接收數(shù)據(jù)的socket方的id
msg 要發(fā)送數(shù)據(jù)的指針
len 以字節(jié)為單位的數(shù)據(jù)的長度
flags 一般為0
返回值 :
失敗 -1
成功 發(fā)送成功的字節(jié)數(shù)
示 例:
char *msg = "Hello!";
int len, bytes_sent;
....
len = strlen(msg);
bytes_sent = send(client_fd, msg,len,0);
...
.recv()
int recv(int sockfd, void *buf, int len, unsigned int flags);
參數(shù)列表:
sockfd 接收數(shù)據(jù)的socket的fd
buf 存放數(shù)據(jù)的數(shù)據(jù)緩沖區(qū)
len len是緩沖的長度
flags 通常為0
返回值 :
成功 實際接收的字節(jié)數(shù)
錯誤 -1
while(1)
{
recvbytes = recv(sockfd, buf, MAXDATASIZE, 0);
if(recvbytes
本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/20619/showart_1134753.html |
|