2016年5月15日 星期日

socket 筆記

以下詳細解釋來自 XYZ的筆記本 大大,自己另外做的筆記。

1.在 Windows 使用 Socket 需要 link Winsock Library。[server端] [client端]
   在程式中加入
   #pragma comment(lib, "wsock32.lib") 或 #pragma comment(lib, "Ws2_32.lib")

   wsock32.lib 和 Ws2_32.lib 的區別:
  • wsock32.lib 是較舊的 1.1 版本,Ws2_32.lib 是較新的 2.0 版本。
  • wsock32.lib 跟 winsock.h 一起使用,Ws2_32.lib 跟 WinSock2.h 一起使用。
  • winsock.h 和 WinSock2.h 不能同時使用,WinSock2.h 是用來取代 winsock.h,而不是擴展 winsock.h。
2.建立 socket descriptor。[server端] [client端]
   函式:SOCKET socket( int af, int type, int protocol); 
          int af:使用何種通訊。
  • 例如
    AF_INET:使用 IPv4
    AF_INET6:使用 IPv6
   int type:The type specification for the new socket.
  • 能用的值跟 " int af " 參數有關
  • 例如
    SOCK_STREAM:使用 TCP 協議
    SOCK_DGRAM:使用 UDP 協議
   int protocol:The protocol to be used.
  • 例如
    IPPROTO_TCP:使用 TCP
    IPPROTO_UDP:使用 UDP
   成功回傳 socket descriptor,失敗回傳 INVALID_SOCKET。可善用 " INVALID_SOCKET " 來檢查是否成功。

3.設定位址資訊的資料 (SOCKADDR_IN)。[server端] [client端]  
   結構:使用 IP4 格式結構 struct sockaddr_in (in 表示 internet) 設定 internet 位址資訊。
  • struct sockaddr_in:IPv4 使用
  • struct sockaddr_in6:IPv6 使用
  • struct sockaddr:通用格式
  • struct sockaddr_un:UNIX domain 格式

      4.綁定 socket 的位址資料 (bind)。[server端]
         函式:int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
         int sockfd:socket() 函式回傳的 socket descriptor
         struct sockaddr *my_addr:用來通訊的位址資料(IP、PORT)
         int addrlen:位址長度 ,sizeof(my_addr)
         綁定成功回傳 0,失敗回傳 SOCKET_ERROR。可善用 " SOCKET_ERROR " 來檢查是否成功。
      5.監聽連線 (listen)。[server端]   函式:int listen(SOCKET s,  int backlog)
      • SOCKET s:socket() 函式回傳的 socket descriptor。
      • int backlog:最大可監聽多少連線(佇列、排隊)。設定 SOMAXCONN 表示系統最大值。
      • 成功回傳 0,失敗回傳 SOCKET_ERROR。
      • connection-oriented ( SOCK_STREAM ) 的 server 程式端程式才使用。
      6.連線到 socket Server。[client端]
         函式:int connect(SOCKET s, const struct sockaddr *name, int namelen)
      • SOCKET s:socket() 函式回傳的 socket descriptor。
      • const struct sockaddr *name:Server 端的位址資料。
      • int namelen:第二個參數的大小。
      • 成功回傳 0,失敗回傳 SOCKET_ERROR,可用 WSAGetLastError() 取得 error code。
      7.傳送訊息。[server端] [client端]   
         函式 1:int send(SOCKET s, const char *buf, int len, int flags)   
         函式 2:int sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen)
      • send() 和 sendto() 的差異在於,send() 只能用在 connection-oriented ( SOCK_STREAM ) 的連線,所以 sendto() 比 send() 多最後兩個參數,用來指定目的地的位址資訊。
      • SOCKET s:socket() 函式回傳的 socket descriptor。
      • const char *buf:訊息的指標。
      • int len:訊息的長度。
      • int flags:The flags parameter can be used to influence the behavior of the function beyond the options specified for the associated socket。(一般設 0)
      MSG_DONTROUTE:不將訊息送給 gateway,而直接送給 host。(Specifies that the data should not be subject to routing. A Windows Sockets service provider can choose to ignore this flag.)
      MSG_OOB:Sends OOB data (stream-style socket such as SOCK_STREAM only)。
      • const struct sockaddr *to:目的地位址資訊。
      • int tolen:目的地位址資訊的大小。
      • 成功回傳傳送的資料長度,失敗回傳 SOCKET_ERROR。
      8.接收訊息。[server端] [client端]   
         函式 1:int recv(SOCKET s, char *buf, int len, int flags)   
         函式 2:int recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
      • recv() 和 recvfrom() 的差異在於,recv() 只能用在 connection-oriented ( SOCK_STREAM ) 的連線,所以 recvfrom() 比 recv() 多最後兩個參數,用來指定接收來源的位址資訊。
      • SOCKET s:socket() 函式回傳的 socket descriptor。
      • const char *buf:訊息的指標。
      • int len:訊息的長度。
      • int flags:The flags parameter can be used to influence the behavior of the function invocation beyond the options specified for the associated socket.。(一般設 0)
      MSG_PEEK:Peeks at the incoming data。只看訊息內容,但不將訊息從 queue 移除。
      MSG_OOB:Processes Out Of Band (OOB) data。
      • const struct sockaddr *to:目的地位址資訊。
      • int tolen:目的地位址資訊的大小。
      • 成功回傳接收的資料長度,連線被關閉回傳 0。失敗回傳 SOCKET_ERROR。
      9.設定 socket option 選項。[server端] [client端]
         函式 :int setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen)
         用來設定建立的  socket 一些特性,例如是否強制關閉等。
      10.關閉 socket。[server端] [client端]
           函式 :int closesocket(SOCKET s)
      ===============================================================
      編譯環境

      win7 + CodeBlocks16.01 + MinGW

      Scoket Server 範例:
      執行後會等待 client 端連線,有連線進來時,則傳送 "sending data test" 訊息給 client 端。



      #pragma comment(lib, "Ws2_32.lib")
      #include <WinSock2.h>
      #include <iostream>
      using namespace std;
      void main(){
          int r;
          WSAData wsaData;
          WORD DLLVSERION;
      char *message;

          DLLVSERION = MAKEWORD(2,1);//Winsocket-DLL 版本
          //用 WSAStartup 開始 Winsocket-DLL
          r = WSAStartup(DLLVSERION, &wsaData);
          //宣告 socket 位址資訊
          SOCKADDR_IN addr;
          int addrlen = sizeof(addr);
          //建立 socket
          SOCKET sListen; //listening for an incoming connection
          SOCKET sConnect; //operating if a connection was found
          //AF_INET:表示建立的 socket 屬於 internet family
          //SOCK_STREAM:表示建立的 socket 是 connection-oriented socket
          sConnect = socket(AF_INET, SOCK_STREAM, NULL);
          //設定位址資訊的資料
          addr.sin_addr.s_addr = inet_addr("127.0.0.1");
          addr.sin_family = AF_INET;
          addr.sin_port = htons(1234);
          //設定 Listen
          sListen = socket(AF_INET, SOCK_STREAM, NULL);
          bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
          listen(sListen, SOMAXCONN);//SOMAXCONN: listening without any limit
          //等待連線
          SOCKADDR_IN clinetAddr;
      c = sizeof(struct sockaddr_in);

          while( (sConnect = accept(sListen , (struct sockaddr *)&clinetAddr, &c)) != INVALID_SOCKET ){
      puts("Connection accepted"); //Reply to the client message = "Hello Client , I have received your connection. But I have to go now, bye\n"; send(sConnect , message , strlen(message) , 0);
      }
      }



      Socket Client 範例:



      #pragma comment(lib, "Ws2_32.lib") 

      #include <WinSock2.h>

      #include <iostream>

      #include <string>


      using namespace std;


      void main(){

          string confirm;

          char server_reply[2000]; 

          //開始 Winsock-DLL

          int r;

          WSAData wsaData;

          WORD DLLVersion;

          DLLVersion = MAKEWORD(2,1);
          r = WSAStartup(DLLVersion, &wsaData);
          //宣告給 socket 使用的 sockadder_in 結構
          SOCKADDR_IN addr;
          int addlen = sizeof(addr);
          //設定 socket
          SOCKET sConnect; 
          //AF_INET: internet-family
          //SOCKET_STREAM: connection-oriented socket
          sConnect = socket(AF_INET, SOCK_STREAM, NULL);
          //設定 addr 資料
          addr.sin_addr.s_addr = inet_addr("127.0.0.1");
          addr.sin_family = AF_INET;
          addr.sin_port = htons(1234);

          connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
          //接收 server 端的訊息
          puts("Connected");
      //Receive a reply from the server if((recv_size = recv(sConnect , server_reply , 2000 , 0)) == SOCKET_ERROR){
      puts("recv failed"); } puts("Reply received\n"); //Add a NULL terminating character to make it a proper string before printing server_reply[recv_size] = '\0'; puts(server_reply);
          //設定 closesocket 時,不經過 TIME-WAIT 過程,直接關閉socket
          //BOOL bDontLinger = FALSE;
          //setsockopt(sConnect,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));             
          //若之後不再使用,可用 closesocket 關閉連線
          closesocket(sConnect);