socket函数
SOCKET()
我們使用系統調用socket()來獲得文件描述符:
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);
第一個參數domain設置為“AF_INET”。
第二個參數是套接口的類型:SOCK_STREAM或
SOCK_DGRAM。第三個參數設置為0。
系統調用socket()只返回一個套接口描述符,如果出錯,則返回-1。
bind()
一旦你有了一個套接口以后,下一步就是把套接口綁定到本地計算機的某一個端口上。但如果你只想使用connect()則無此必要。
下面是系統調用bind()的使用方法:
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,struct sockaddr*my_addr,int addrlen);
第一個參數sockfd是由socket()調用返回的套接口文件描述符。
第二個參數my_addr是指向數據結構sockaddr的指針。數據結構sockaddr中包括了關于你的地址、端口和IP地址的信息。
第三個參數addrlen可以設置成sizeof(structsockaddr)。
下面是一個例子:
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#defineMYPORT3490
main()
{
int sockfd;
struct sockaddr_inmy_addr;
sockfd=socket(AF_INET,SOCK_STREAM,0);/*do someerror checking!*/
my_addr.sin_family=AF_INET;/*hostbyteorder*/
my_addr.sin_port=htons(MYPORT);/*short,network byte order*/
my_addr.sin_addr.s_addr=inet_addr("132.241.5.10");
bzero(&(my_addr.sin_zero),8);/*zero the rest of the struct*/
/*don't forget your error checking for bind():*/
bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));
...
如果出錯,bind()也返回-1。
如果你使用connect()系統調用,那么你不必知道你使用的端口號。當你調用connect()時,它檢查套接口是否已經綁定,如果沒有,它將會分配一個空閑的端口。
Connect()
系統調用connect()的用法如下:
#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,struct sockaddr* serv_addr,int addrlen);
第一個參數還是套接口文件描述符,它是由系統調用socket()返回的。
第二個參數是serv_addr是指向數據結構sockaddr的指針,其中包括目的端口和IP地址。
第三個參數可以使用sizeof(structsockaddr)而獲得。
下面是一個例子:
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#defineDEST_IP"132.241.5.10"
#defineDEST_PORT23
main()
{
intsockfd;
structsockaddr_indest_addr;/*will hold the destination addr*/
sockfd=socket(AF_INET,SOCK_STREAM,0);/*do some error checking!*/
dest_addr.sin_family=AF_INET;/*hostbyteorder*/
dest_addr.sin_port=htons(DEST_PORT);/*short,network byte order*/
dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);
bzero(&(dest_addr.sin_zero),8);/*zero the rest of the struct*/
/*don'tforgettoerrorchecktheconnect()!*/
connect(sockfd,(structsockaddr*)&dest_addr,sizeof(struct sockaddr));
...
同樣,如果出錯,connect()將會返回-1。
listen()
如果你希望不連接到遠程的主機,也就是說你希望等待一個進入的連接請求,然后再處理它們。這樣,你通過首先調用listen(),然后再調用accept()來實現。
系統調用listen()的形式如下:
intl isten(int sockfd,int backlog);
第一個參數是系統調用socket()返回的套接口文件描述符。
第二個參數是進入隊列中允許的連接的個數。進入的連接請求在使用系統調用accept()應答之前要在進入隊列中等待。這個值是隊列中最多可以擁有的請求的個數。大多數系統的缺省設置為20。你可以設置為5或者10。當出錯時,listen()將會返回-1值。
當然,在使用系統調用listen()之前,我們需要調用bind()綁定到需要的端口,否則系統內核將會讓我們監聽一個隨機的端口。所以,如果你希望監聽一個端口,下面是應該使用的系統調用的順序:
socket();
bind();
listen();
/*accept()goeshere*/
accept()
系統調用accept()比較起來有點復雜。在遠程的主機可能試圖使用connect()連接你使用
listen()正在監聽的端口。但此連接將會在隊列中等待,直到使用accept()處理它。調用accept()
之后,將會返回一個全新的套接口文件描述符來處理這個單個的連接。這樣,對于同一個連接
來說,你就有了兩個文件描述符。原先的一個文件描述符正在監聽你指定的端口,新的文件描
述符可以用來調用send()和recv()。
調用的例子如下:
#include<sys/socket.h>
intaccept(intsockfd,void*addr,int*addrlen);
第一個參數是正在監聽端口的套接口文件描述符。第二個參數addr是指向本地的數據結構
sockaddr_in的指針。調用connect()中的信息將存儲在這里。通過它你可以了解哪個主機在哪個
端口呼叫你。第三個參數同樣可以使用sizeof(structsockaddr_in)來獲得。
如果出錯,accept()也將返回-1。下面是一個簡單的例子:
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#defineMYPORT3490/*theportuserswillbeconnectingto*/
#defineBACKLOG10/*howmanypendingconnectionsqueuewillhold*/
main()
{
intsockfd,new_fd;/*listenonsock_fd,newconnectiononnew_fd*/
structsockaddr_inmy_addr;/*myaddressinformation*/
structsockaddr_intheir_addr;/*connector'saddressinformation*/
intsin_size;
sockfd=socket(AF_INET,SOCK_STREAM,0);/*dosomeerrorchecking!*/
my_addr.sin_family=AF_INET;/*hostbyteorder*/
my_addr.sin_port=htons(MYPORT);/*short,networkbyteorder*/
my_addr.sin_addr.s_addr=INADDR_ANY;/*auto-fillwithmyIP*/
bzero(&(my_addr.sin_zero),8);/*zerotherestofthestruct*/
/*don'tforgetyourerrorcheckingforthesecalls:*/
bind(sockfd,(structsockaddr*)&my_addr,sizeof(structsockaddr));
listen(sockfd,BACKLOG);
sin_size=sizeof(structsockaddr_in);
new_fd=accept(sockfd,&their_addr,&sin_size);
...
下面,我們將可以使用新創建的套接口文件描述符new_fd來調用send()和recv()。
send() 和recv()
系統調用send()的用法如下:
int send(int sockfd,const void* msg,int len,int flags);
第一個參數是你希望給發送數據的套接口文件描述符。它可以是你通過socket()系統調用返回的,也可以是通過accept()系統調用得到的。
第二個參數是指向你希望發送的數據的指針。
第三個參數是數據的字節長度。第四個參數標志設置為0。
下面是一個簡單的例子:
char*msg="Beejwashere!";
intlen,bytes_sent;
..
len=strlen(msg);
bytes_sent=send(sockfd,msg,len,0);
...
系統調用send()返回實際發送的字節數,這可能比你實際想要發送的字節數少。如果返回的字節數比要發送的字節數少,你在以后必須發送剩下的數據。當send()出錯時,將返回-1。
系統調用recv()的使用方法和send()類似:
int recv(int sockfd,void* buf,int len,unsigned int flags);
第一個參數是要讀取的套接口文件描述符。
第二個參數是保存讀入信息的地址。
第三個參數是緩沖區的最大長度。第四個參數設置為0。
系統調用recv()返回實際讀取到緩沖區的字節數,如果出錯則返回-1。
這樣使用上面的系統調用,你可以通過數據流套接口來發送和接受信息。
sendto() 和recvfrom()
因為數據報套接口并不連接到遠程的主機上,所以在發送數據包之前,我們必須首先給出目的地址,請看:
int sendto(int sockfd,const void* msg,int len,unsigned int flags,
conststruct sockaddr*to,inttolen);
除了兩個參數以外,其他的參數和系統調用send()時相同。
參數to是指向包含目的IP地址和端口號的數據結構sockaddr的指針。
參數tolen可以設置為sizeof(structsockaddr)。
系統調用sendto()返回實際發送的字節數,如果出錯則返回-1。
系統調用recvfrom()的使用方法也和recv()的十分近似:
int recvfrom(int sockfd,void* buf,int len,unsigned int flags
struct sockaddr* from,int* fromlen);
參數from是指向本地計算機中包含源IP地址和端口號的數據結構sockaddr的指針。
參數fromlen設置為sizeof(struct sockaddr)。
系統調用recvfrom()返回接收到的字節數,如果出錯則返回-1。
close() 和shutdown()
你可以使用close()調用關閉連接的套接口文件描述符:
close(sockfd);
這樣就不能再對此套接口做任何的讀寫操作了。
使用系統調用shutdown(),可有更多的控制權。它允許你在某一個方向切斷通信,或者切斷雙方的通信:
int shutdown(int sockfd,int how);
第一個參數是你希望切斷通信的套接口文件描述符。第二個參數how值如下:
0—Furtherreceivesaredisallowed
1—Furthersendsaredisallowed
2—Furthersendsandreceivesaredisallowed(likeclose())
shutdown()如果成功則返回0,如果失敗則返回-1。
?getpeername()
這個系統的調用十分簡單。它將告訴你是誰在連接的另一端:
#include<sys/socket.h>
int getpeername(int sockfd,struct sockaddr* addr,int* addrlen);
第一個參數是連接的數據流套接口文件描述符。
第二個參數是指向包含另一端的信息的數據結構sockaddr的指針。
第三個參數可以設置為sizeof(structsockaddr)。
如果出錯,系統調用將返回-1。
一旦你獲得了它們的地址,你可以使用inet_ntoa()或者gethostbyaddr()來得到更多的信息。?gethostname()
系統調用gethostname()比系統調用getpeername()還簡單。它返回程序正在運行的計算機的名字。系統調用gethostbyname()可以使用這個名字來決定你的機器的IP地址。
下面是一個例子:
#include<unistd.h>
int gethostname(char*hostname,size_tsize);
如果成功,gethostname將返回0。如果失敗,它將返回-1。
-----------------------------------------------------------------------------------------------------------------------------------------------------------
基本TCP套接口編程一
本文出自:http://sunsland.top263.net 作者: (2001-10-22 12:00:00)
概述
socket() --得到文件描述符!
bind() --我們在哪個端口?
connect() --Hello!
listen() --有人給我打電話嗎?
accept() --"Thank you for calling port 3490."
send() 和 recv() --Talk to me, baby!
sendto() 和 recvfrom() --Talk to me, DGRAM-style
close() 和 shutdown() --滾開!
getpeername() --你是誰?
gethostname() --我是誰?
DNS --你說“白宮”,我說 "198.137.240.100"
--------------------------------------------------------------------------------
socket函數
功能:指定協議類型
定義:
#include
#include
int socket(int family, int type, int protocol);
返回值
出錯: -1
成功: 套接口描述字 (socket file descriptor)(套接字)sockfd
socket 函數指定了協議族(IPv4、IPv6或unix)和套接口類型(字節流、數據報或原
始套接口)。但并沒有指定本地協議地址或遠程協議地址。
理解socket
socket使用 Unix 文件描述符 (file descriptor) 和其他程序通訊的方式。
Unix 程序在執行任何形式的 I/O 的時候,程序是在讀或者寫一個文件描述符。
一個文件描述符只是一個和打開的文件相關聯的整數。
這個文件可能是一個網絡連接,FIFO,管道,終端,磁盤上的文件或者什么其他
的東西。Unix 中所有的東西是文件!因此,與 Internet 上別的程序通訊的時候,
要通過文件描述符。利用系統調用 socket()得到網絡通訊的文件描述符。他返回
套接口描述符 (socket descriptor),然后再通過他來調用 send() 和 recv()。
那么為什么不用一般的調用 read() 和 write() 來通過套接口通訊?
簡單的答案是:可以使用一般的函數!
詳細的答案是:使用 send() 和 recv() 讓你更好的控制數據傳輸。
--------------------------------------------------------------------------------
connect 函數
功能:建立與TCP服務器的連接
定義:
#include
#include
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
//sockfd 是系統調用 socket() 返回的套接口文件描述符
serv_addr 是保存著目的地端口和 IP 地址的數據結構 struct sockaddr
//addrlen 設置為 sizeof(struct sockaddr)
connect 激發 TCP的三路握手過程
服務器必須準備好接受外來的連接。
這通過調用socket,bind和1isten函數來完成,稱為被動打開(Passive open)
客戶通過調用connect進行主動打開(active opn)。
這引起客戶TCP發送一個SYN分節(表示同步),它告訴服務器客戶將在(待建立的)
連接中發送的數據的初始序列號。
服務器必須確認客戶的SYN,同時自己也得發送一個SYN分節,它含有服務器將在
同一連接中發送的數據的韌始序列號。服務器以單個分節向客戶發送SYN和對客戶
SYN的ACK。客戶必須確認服務器的SYN。
connect 出錯時的返回
出錯原因 :未收到SYN的響應(服務器超時,75s)
返回值:ETIMEDOUT
用戶端輸出:Connection time out.
出錯原因 :收到RST響應(Hard error)SYN到達服務器,但該服務器的無此項端口服務
返回值:ECONNREFUSE
用戶端輸出:Connection refused
出錯原因 :ICMP錯誤:不可路由(soft error)(目的地不可達)
返回值:EHOSTUNREACH
用戶端輸出:ENETUNREACH No route to host
--------------------------------------------------------------------------------
bind 函數
功能:給套接口分配一個本地協議地址
定義:
#include
#include
int bind(int sockfd, const struct sockaddr *my_addr, int addrlen);
sockfd 是調用 socket 返回的文件描述符。
my_addr 是指向數據結構 struct sockaddr 的指針,保存地址(即端口和 IP 地址) 信息。
addrlen 設置為 sizeof(struct sockaddr)。
返回: 0—成功, -1---出錯
讓內核自動處理地址ip和端口port
my_addr.sin_port = 0; /* choose an unused port at random */
my_addr.sin_addr.s_addr = INADDR_ANY; /* use my IP address */
bind( ) 自己選擇合適的端口:將0賦給 my_addr.sin_por。
自動填上他所運行的機器的 IP 地址:my_addr.sin_addr.s_addr 設置為 INADDR_ANY。
--------------------------------------------------------------------------------
listen 函數
功能:將未連接主動套接口的轉換為被動套接口,指示內核接受對該套接口的連接請求.
CLOSED --? LISTEN
定義:
#include
int listen(int sockfd, int backlog);
sockfd 是調用 socket() 返回的套接口文件描述符。
backlog 是在進入隊列中允許的連接數目。
監聽套接口的兩個隊列
未完成連接隊列(incompleted connection queue): SYN_RECV
已完成連接隊列(completed connection queue): ESTABLISHED
當一個客戶的SYN到達時,如兩隊列都滿的, TCP將忽略該分節且不發RST
--------------------------------------------------------------------------------
ACCEPT 函數
功能:在已完成隊列頭返回下一個已完成的連接
定義
#include
int accept(int sockfd, struct sockaddr *cliaddr, int* addrlen);
調用成功時返回: 1. cliaddr: 客戶進程的協議地址和地址大小 2. 新套接口描述字
(已連接套接口描述字)
監聽套接口描述字 listening socket descriptor
一個給定的服務器常常是只生成一個監聽套接口, 且一直存在,直到該服務器關閉。
已連接套接口描述字connected socket descriptor
內核為每個被接受的客戶連接創建了一個已連接套接口。當服務器完成某客戶的服務時,
關閉已連接套接口。
1024以下的端口:超級用戶使用
--------------------------------------------------------------------------------
fork 函數
功能:派生新進程 create new process
定義:
#include
pid_t fork (void);
在子進程中返回0,在父進程中返回子進程的進程ID
出錯時返回 –1,調用一次返回兩次
fork的典型應用:
1.一個進程可為自己創建一個拷貝。當一個拷貝處理一個操作時,其他的拷貝可以
執行其他的任務。這是非常典型的網絡服務器。
2.一個進程想執行其他的程序,由于創建新進程的唯一方法是調用fork,進程首先
調 用fork來生成一個拷貝,然后其中一個拷貝(通常為子進程)調用exec 來代替自己
去執行新程序。
(http://www.fanqiang.com)
-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------
并發服務器
迭代服務器 iterative server 單進程
并發服務器 concurrent server 多進程
當連接建立時,accept返回,服務器調用fork
子進程為客戶提供服務(通過connfd已連接套接口),
父進程等待另一個連接(通過listenfd監聽套接口)。
子進程開始處理新客戶后,父進程便關閉已連接套接口。
每個文件或套接口都有一個訪問計數,該訪問計數在文件表項中維護,它表示當前指
向該文件或套接口的打開的描述字個數。
從sock返回后,與listenfd關聯的文件表項訪問計數值為1,
從accept返回后,與connfd關聯的文件表項訪問計數值也為1。
當fork返回后,兩個描述字在父進程與子進程間共享(duplicated)與兩個套接門相關
聯的文件表項訪問計數值均為2。
當父進程關閉connfd時,只是將訪問計數值從2減為1。描述字只在訪問計數值達到0時
才真正關閉,這在后面某個時候子進程關閉connfd時會碰上.
--------------------------------------------------------------------------------
close 函數
功能:將套接口做上“已關閉的標記”,并立即返回進程。
將套接口描述字的訪問計數器減1。
當訪問計數器值為0時,引發TCP的四個分組連接終止序列,從而關閉套接口。
定義:
#include
int close(int sockfd);
--------------------------------------------------------------------------------
getsockname 和 getpeername函數
功能:
getsockname: 返回本地協議地址
getpeername:返回遠程協議地址
定義:
#include
int getsockname (int sockfd, struct sockaddr *localaddr, int *addrlen);
int getpeername(int sockfd, struct sockaddr *peeraddr, int *addrlen);
--------------------------------------------------------------------------------
一個示例程序:
#include
#include
#include
#define MYPORT 3490 /* the port users will be connecting to */
#define BACKLOG 10 /* how many pending connections queue will hold */
main()
{
int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */
struct sockaddr_in my_addr; /* my address information */
struct sockaddr_in their_addr; /* connector's address information */
int sin_size;
sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */
my_addr.sin_family = AF_INET; /* host byte order */
my_addr.sin_port = htons(MYPORT); /* short, network byte order */
my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */
/* don't forget your error checking for these calls: */
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
listen(sockfd, BACKLOG);
sin_size = sizeof(struct sockaddr_in);
new_fd = accept(sockfd, &their_addr, &sin_size);
總結
- 上一篇: Diango REST framewor
- 下一篇: SICP学习笔记(P27-P28)