TCP/IP 分層架構


Socket

socket介於應用層與傳輸層之間 ,是一個網路系統的通訊函式庫,在任何作業系統中可以通用

   主要的函式:

    socket()    <->   電話

    bind()   <->  線路(第幾分機?)

    listen()  <->  準備好接聽(啟用鈴聲)

    connect()   <->  撥電話出去

    accept()    <->  接聽

  send()/recv()  <-> 通話

  closesocket()  <-> 掛斷

在快樂的使用函式之前,有幾件事情要先注意


 

#include<winsock2.h>

winsock2.h內定義了一些有用的巨集以及socket資料結構

 

int WSAStartup( WORD wVersionRequested,LPWSADATA lpWSAData );


範例:出自msdn library:http://msdn2.microsoft.com/en-us/library/ms742213.aspx

WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
return 1;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return 1;
}
/* The WinSock DLL is acceptable. Proceed. */

 

int WSACleanup( void );

 


開始使用socket

int socket( int af,  int type, int protocol);

範例:

  SOCKET sock;             //宣告

  sock=socket(AF_INET,SOCK_STREAM,0);  //設定

 

int bind(  SOCKET s,  const struct sockaddr* name,  int namelen);

sockaddr原型(保留各協定自行定義空間)

struct sockaddr{
u_short sa_family; // address family
char sa_data[14]; // 位址內容(未定義)
}

sockaddr for IPv4:

struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8]; };

sin_family:位址資料族系,同樣設定為AF_INET
sin_port:主機開啟的通訊埠號 用htons() 寫入
sin_addr:主機IP位址 in_addr資料格式
sin_zero[8]:目前沒用處,保留以後使用

範例:

struct sockaddr_in sa;      //先宣告一個sockaddr_in作為引入值     
sa.sin_family=AF_INET;      //設定為internet位址族系   
sa.sin_port=htons(31800);    //htons()是將數字轉換成網路位元排列   
sa.sin_addr.s_addr=INADDR_ANY; //INADDR_ANY表示我不在意Local IP,由系統自行決定
                     //若要指定IP,則使用inet_addr,例如:name.sin_addr.s_addr = inet_addr(“140.115.65.1”);
                     //將IP字串轉為網路位元排列,如需要反轉換,有inet_ntoa函式可用
bind(sock,(sockaddr *)&sa,sizeof(sa)); //bind
 

int listen(SOCKET s,  int backlog);

範例:listen(sock,1); 

 

SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);

傳進accept()的listen socket本身並沒有辦法作資料的傳輸, 所以必須透過accept()產生一個包含通訊協定、Server、Client資訊的新socket,利用他就可以進行資料的傳輸了

範例:accept(Message.WParam,NULL,NULL);

 

int connect(SOCKET s,const struct sockaddr* name,int namelen);

設定方式請參照bind()函式

  回傳值:-1表錯誤,否則回傳0

 

  int recv(SOCKET s, char* buf, int len, int flags);

範例:

#defined MSG_LEN 100       //定義接收訊息的最大長度    
//----------------------------------------------------------------------------------------    
int bytes_of_read;         //宣告變數儲存recv的回傳值    
char buf[MSG_LEN];        // 宣告暫存區    
bytes_of_read=recv(sock,buf,1024,0); //recv

 

 

int send(  SOCKET s,const char* buf, int len, int flags);

範例:

int bytes_of_sent;              //宣告變數儲存recv的回傳值 bytes_of_sent=recv(sock,Edit1->Text.c_str(),Edit1->Text.Length,0); //send

 

 

  int closesocket(SOCKET s);

  int shutdown(SOCKET s,int how);

一個好的中斷連線作法應有四步:

  1. 結束傳送資料
  2. 使用shutdown(),設定為禁止送出資料
  3. 呼叫recv(),確定收到的資料長度為0,避免遺漏資訊 
  4. closesocket() 來關閉socket

範例:

void __fastcall TForm1::SocketGracefulClose(SOCKET s)       
{  
 int i=0;
 bool flag=true;  
 AnsiString buf;  
 shutdown(s,SD_SEND);  
 i=recv(s,buf.c_str(),1024,0);  
 if(i==-1);  
 else
 {   
  while(flag)
  {    
   i=recv(s,buf.c_str(),1024,0);    
   if(i==0)    
   {     
    flag=false;    
   }   
  }  
 }   
 closesocket(s); 
//Memo1->Lines->Add("socket closed"); 
}