套接字編程--TCP

一、socket編程

    socket本身有“插座“的意思,因此用來描述網絡連接的一對一關係。”在TCP/IP協議中,“TP地址+TCP或端口號”唯一標識網絡通訊中的一個進程,“IP地址+端口號”就稱爲socket。(socket就像當於文件一樣,客戶端通過往裏面寫數據,服務器端就從裏面讀取數據,socket 就是用來做載體的)。爲TCP/TP協議設計的應用層編程接口稱爲socketAPI.

二、網絡字節序

    內存中的多字節數據相對於內存地址有大端和小端之分,磁盤文件中的多字節數據相對於文件中的偏移地址也有大端小端之分。網絡數據流同樣有大端小端之分,那麼如何定義網絡數據流的地址呢?發送主機通常將發送緩衝區中的數據按內存地址從低到高的順序發出,接收主機把從網絡上接到的字節依次保存在接收緩衝區中,也是按內存地址從低到高的順序保存,因此,網絡數據流的地址應這樣規定:先發出的數據是低地址,後發出的數據是高地址。TCP/IP協議規定,網絡數據流應採用大端字節序,(即低地址存放在高字節)    

    例如地址0-1是16位的源端口號,如果這個端口號是1000(0x3e8),則地址0是0x03,地址1是0xe8, 也就是先發0x03,再發0xe8,這16位在發送主機的緩衝區中也應該是低地址存0x03,高地址存0xe8。但是,如果發送主機是小端字節序的,這16位被解釋成0xe803,而不是1000。因此,發送主機把1000填到發送緩衝區之前需要做字節序的轉換。同樣地,接收主機如果是小端字節序的, 接到16位的源端口號也要做字節序的轉換。如果主機是大端字節序的,發送和接收都不需要做轉換。同理,32位的IP地址也要考慮網絡字節序和主機字節序的問題.

    爲使網絡程序具有可移植性,使同樣的C代碼在大端和小端計算機上編譯後都能正常運行,可以調用以下庫函數做網絡字節序和主機字節序的轉換

    uint32_t htonl(uint32_t hostlong);

    uint16_t htons(uint16_t hostshort);

    uint32_t ntohl(uint32_t netlong);

    uint16_t ntohs(uint16_t netshort);

*************** h表示host,n表示network,l表示long,s表示short.例如:htonl表示將32位的長整數從主機字節序轉換爲網絡字節序,******************       

三、socket地址的數據類型及其相關函數

    ①、 sockaddr數據結構

        1、struct sockaddr(這就相當於void*類型)

        2、struct sockaddr_in

            IPV4地址用 sockaddr_in結構體表示,包括16位端口號和32位IP 地址;IPV6地址用 sockaddr_in6結構體表示,包括16位端口號、128位IP地址和一些控制字段。

    ②、綁定函數(bind)

        綁定是將創建出來的socket和端口號綁定在一起,是這個創建出來的socket監聽地址和端口號。

    ③、字符串轉in_addr的函數

        int inet_aton(const char* strptr,struct in_addr * addrptr);

        int_addr_t inet_addr(const char* strptr);//將點分十進制的IP地址轉換成32位的無符號整形

        int inet_pton(int family,const char* strptr,void* addrptr);

    ④、in_addr轉字符串的函數

        char* inet_ntoa(struct in_addr inaddr);//將整形轉換成點分十進制的IP地址

        const char* inet_ntop(int family,const void* addrptr,char * strptr,size_t len);       

     ⑤、監聽(listen)

        對於服務器端來說,需要一直保持一個監聽的狀態時刻監聽網絡中是否有連接請求,

    ⑥、接收(accept)

        對於服務器端,當有連接請求的時候,需要一個套接字用於處理請求。

    ⑦、連接(conncect)

        客戶端用於發送請求連接的函數

—————————————————————————————————————————————

用例子說明:

一、服務器端:

    ①、首先創建套接字

    ②、添加本地信息(struct sockaddr_in)

    ③、綁定端口號(bind)

    ④、服務器監聽是否有連接(listen)

    ⑤、接收穫取連接(accepet)

   #include<stdio.h>
   #include<stdlib.h>
   #include<sys/types.h>
   #include<sys/socket.h>
   #include<netinet/in.h>
   #include<arpa/ftp.h>
   #include<pthread.h>
   #include<string.h>
   static int start(char *_ip,int _port)
  {
      int listen_sock =socket(AF_INET,SOCK_STREAM,0);//創建套接字
      if(listen_sock < 0)
      {
          printf("socket");
          exit(1);
      }
      struct sockaddr_in local;//添加本地信息
      local.sin_family=AF_INET; //協議家族
      local.sin_port=htons(_port);//端口號
      local.sin_addr.s_addr=inet_addr(_ip);//IP地址
  
      if(bind(listen_sock ,(struct sockaddr *)&local,sizeof(local)) < 0)//綁定
      {
          printf("bind");
          exit(2);
      }
      if(listen(listen_sock ,5) < 0 )//監聽
      {
          perror("listen");
          exit(3);
      }
      return listen_sock ;
  
  }
  void usage(char *proc)
  {
      printf("usage :%s[ip][port]\n",proc);
  }
  int main(int argc,char * argv[])
  {
      if(argc != 3)
      {
          usage(argv[0]);
          exit(1);
      }
  
      int port= atoi(argv[2]);
      int sock=start(argv[1],port);
      struct sockaddr_in client;
      socklen_t len =sizeof(client);
      while(1)
      {
          int new_fd = accept(sock  ,(struct sockaddr*)&client,&len);
          if(new_fd <0)
          {
              printf("accept\n");
              continue;
          }
          printf("get aconnect... sock : %d,prot:%d ,ip:%s \n"
           ,new_fd,ntohs(client.sin_port),inet_ntoa(client.sin_addr));
 
          char buf[1024];
          while(1)
          {
              size_t size=read(new_fd,buf,sizeof(buf)-1);
            if(size > 0 )//read success
            {
               buf[size]='\0';
             }
            else if(size == 0)
            {
                printf("client close\n");
               break;
            }
           else
            {
                perror("read\n");
          }
            printf("client: %s \n",buf);
        }

二、客戶端

    ①、創建套接字

    ②、添加本地信息

    ③、建立連接

    ④、寫入數據進行發送

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 #include<sys/socket.h>
  6 #include<sys/types.h>
  7 #include<netinet/in.h>
  8 #include<error.h>
  9 #include<arpa/inet.h>
 10 void usage(char * _proc)
 11 {
 12     printf("usge:%s[remote ip]remote port[]\n",_proc);
 13 }
 14 int main(int argc,char* argv[])
 15 {
 16     if(argc !=3)
 17     {
 18         usage(argv[0]);
 19         exit(1);
 20     }
 21     int r_port=atoi(argv[2]);
 22     char* r_ip=argv[1];
 23 
 24     int sock = socket(AF_INET,SOCK_STREAM,0);//創建套接字
 25     if(sock < -1)
 26     {
 27         perror("sock");
 28         exit(1);
 29     }
 30     struct sockaddr_in  remote;//添加本地信息
 31     remote.sin_family = AF_INET;
 32     remote.sin_port= htons(r_port);
 33     remote.sin_addr.s_addr = inet_addr(r_ip);
 34 
 35     int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote));//建立連接
 36     if(ret<0)
 37     {
 38         printf("connetct\n");
 39     }
 40     char buf[1024];
 41     memset(buf,0,sizeof(buf));
 42     while(1)        
 43     {
 44         printf("please enter::");
 45         scanf("%s",&buf);
 46         write(sock,buf,sizeof(buf)-1);//寫入數據
 47     }
 48 
 49     return 0;
 50 }

結果如下:

    wKioL1dAB23wiJfmAAG8p6ll_No929.jpg總結:

    socket套接字編程簡單的來說就是創建一個公共的載體,客戶端可以通過這個載體向服務器端發送數據,上面的這個程序,服務器一次只能接受一個客戶端的連接,效率是很低下的;TCP是面向連接的,能夠保證信息的可靠性,而UDP是無連接的。可靠性並不能夠保證

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章