Socket編程--基礎(基本server/client實現)


  • IPv4套接口地址結構
    •   IPv4套接口地址結構通常也稱為“網際套接字地址結構”,它以“sockaddr_in”命名,定義在頭文件中
    •   LINUX結構下的常用結構,一般創建套接字的時候都要將這個結構里面的值進行初始化
    • struct sockaddr_in  
      {  
          sa_family_t    sin_family; /* address family: AF_INET */ 
          in_port_t      sin_port;   /* port in network byte order(網絡字節序) */ 
          struct in_addr sin_addr;   /* internet address */ 
      };  
      /* Internet address. */ 
      struct in_addr  
      {  
          uint32_t       s_addr;     /* address in network byte order */ 
      };  

       

  • socket函數 
    • #include <sys/types.h>  
      #include <sys/socket.h>  
      int socket(int domain, int type, int protocol);  

      //創建一個套接字用於通信 參數分析
      domain:指定通信協議族(protocol family),常用取值AF_INET(IPv4)
      type:指定socket類型, 流式套接字SOCK_STREAM,數據報套接字SOCK_DGRAM,原始套接字SOCK_RAW
      protocol:協議類型,常用取值0, 使用默認協議
      返回值:
      成功: 返回非負整數,套接字;
      失敗: 返回-1
  • bind函數
    • int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);  //addrlen--地址長度
      綁定一個本地地址到套接字。綁定函數
      參數:
      sockfd:socket函數返回的套接字
      addr:要綁定的地址

       

  • listen函數
    • int listen(int sockfd, int backlog);
        listen函數應該用在調用socket和bind函數之后, 並且用在調用accept之前, 用於將一個套接字從一個主動套接字轉變成為被動套接字。
      使得一個進程可以接受其它進程的請求,從而成為一個服務器進程。在TCP服務器編程中listen函數把進程變為一個服務器,並指定相應的套接字變為被動連接。

      sockfd 一個已綁定未被連接的套接字描述符
      backlog說明:
      對於給定的監聽套接口,內核要維護兩個隊列:
      1、已由客戶發出並到達服務器,服務器正在等待完成相應的TCP三路握手過程(SYN_RCVD狀態)
      2、已完成連接的隊列(ESTABLISHED狀態)
      但是兩個隊列長度之和不能超過backlog


       
                  

       

  • accept函數
    • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

      sockfd:服務器套接字
      addr:將返回對等方的套接字地址, 不關心的話, 可以設置為NULL
      addrlen:返回對等方的套接字地址長度, 不關心的話可以設置成為NULL, 否則一定要初始化

       

  • connect函數
    • int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

      建立一個連接至addr所指定的套接字
      參數:
      sockfd:未連接套接字
      addr:要連接的套接字地址
      addrlen:第二個參數addr長度

       

  • inet_aton
    •   將一個字符串的ip地址字節轉換成了一個一個32位的網絡序列IP地址
      int inet_aton(const char *string, struct in_addr*addr);

      string是表示的是要轉換的字符串ip地址,后面的是網絡序列ip地址
      成功返回非0,不成功返回-1

       

  • 客戶端和服務器端練習(server\client)
    •   分別建立兩個工程,一個server,一個client。然后先打開服務端,再打開客戶端進行測試,源代碼如下。
      //服務器端
      #pragma comment(lib,"ws2_32.lib")
      #include<iostream>
      #include<winsock2.h>
      #include<string>
      #include<stdio.h>
      using namespace std;
      
      int main(){
          //創建套接字
          WORD myVersionRequest;
          WSADATA wsaData;                          //包含系統所支持的winsocket版本信息
          myVersionRequest = MAKEWORD(1, 1);        //初始化版本1.1
          int err;
          err = WSAStartup(myVersionRequest, &wsaData);
      
          if (!err){
              cout << "已經打開套接字" << endl;
          }
          else{
              //進一步綁定套接字
              cout << "套接字未打開" << endl;
              return 0;
          }
      
          //創建可識別套接字  調用socket函數
          SOCKET serSocket = socket(AF_INET, SOCK_STREAM, 0);
      
          //需要綁定的參數
          SOCKADDR_IN addr;
          addr.sin_family = AF_INET;
          addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//ip地址
          addr.sin_port = htons(3000);//綁定端口
      
          //將套接字綁定到指定的網絡地址
          bind(serSocket, (SOCKADDR*)&addr, sizeof(SOCKADDR));//綁定完成
          //監聽連接
          listen(serSocket, 10);                               //第二個參數代表能夠接受的最多的連接數
          //建立客戶端的連接
          SOCKADDR_IN clientsocket;
          int len = sizeof(SOCKADDR);
          SOCKET serConn;
          //等待客戶端的連接
          serConn = accept(serSocket, (SOCKADDR*)&clientsocket, &len);      //返回一個數字  接受函數,不是-1就是接受連接成功
          cout << "客戶端" << inet_ntoa(clientsocket.sin_addr) << "已連接" << endl;             //客戶端已連接
      
          while (1) {
              char sendBuf[100];                              //發送的字節
              sprintf_s(sendBuf, "server : welcome %s to server.", inet_ntoa(clientsocket.sin_addr));    //轉換函數,將二進制的ip序列轉換成char數組
              //在對應的IP處並且將這行字打印到那里
              send(serConn, sendBuf, strlen(sendBuf) + 1, 0);
              char receiveBuf[100];                          //收到的字節
              //接收客戶端傳來的信息
              recv(serConn, receiveBuf, strlen(receiveBuf) + 1, 0);
              char* quit = "quit";
              //如果客戶端傳來了quit信號,則服務端關閉,客戶端也關閉
              if (!strcmp(receiveBuf, quit)) {
                  break;
              }
              printf("%s\n", receiveBuf);
          }
      
          closesocket(serConn);   //關閉
          WSACleanup();           //釋放資源的操作
      }

       

      //客戶端
      #pragma comment(lib,"ws2_32.lib")
      #include<winsock2.h>
      #include<iostream>
      #include<string>
      #include<conio.h>
      using namespace std;
      int main(){
      
          WORD versionRequired;
          WSADATA wsaData;                            //包含系統所支持的WinStock版本信息
          versionRequired = MAKEWORD(1, 1);           //初始化版本1.1
          int err;
          //注冊WinStock,返回狀態
          err = WSAStartup(versionRequired, &wsaData);//協議庫的版本信息
          if (!err)   {                                //返回結果為0表示初始化失敗{
              cout << LPSTR("客戶端套接字已經打開!\n");
      }
          else{
              //調用WSAGetLastError()查看錯誤信息
              cout << ("客戶端套接字打開失敗:") << WSAGetLastError() << endl;
              return 0;//結束
          }
          /*
          創建套接字:
          流式套接字:   SOCK_STREAM , IPPROTO_TCP
          數據報套接字:  SOCK_DGRAM  , IPPROTO_UDP
          */
      
          SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    //創建流式套接字
          SOCKADDR_IN clientsock_in;                                          //專門針對Internet 通信域的Winsock地址結構
          clientsock_in.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");        //通過inet_addr結構指定套接字的主機IP地址 
          clientsock_in.sin_family = AF_INET;                                 //指定協議家族:AF_INET
          clientsock_in.sin_port = htons(3000);                               //指定將要分配給套接字的傳輸層端口號:6000
      
          //開始連接
          int fail = connect(clientSocket, (SOCKADDR*)&clientsock_in, sizeof(SOCKADDR));
          if (fail){
              cout << "與服務端連接失敗!程序將退出..." << endl;
              _getch();
              return 0;
          }
          string s;
          while (cin >> s){
              char receiveBuf[100];
              //接收數據
              recv(clientSocket, receiveBuf, 101, 0);
              cout << receiveBuf << endl;
              //發送數據
              send(clientSocket, s.c_str(), s.length() + 1, 0);
              if (s == "quit"){
                  break;
              }
          }
          closesocket(clientSocket);//關閉套接字
      
          if (WSACleanup() == SOCKET_ERROR){
              cout << "套接字關閉失敗:" << WSAGetLastError() << endl;
          }
          else{
              cout << "套接字成功關閉." << endl;
          }
          _getch();
          return 0;
      }

       

    •  

        程序運行截圖 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM