1. 程式人生 > >關於socket應用:一個不斷監聽一個程序的伺服器以及傳送資訊的客戶端 TCP的三次握手和四次揮手

關於socket應用:一個不斷監聽一個程序的伺服器以及傳送資訊的客戶端 TCP的三次握手和四次揮手

       大端節序:高位節放在低地址

       小端節序:高位節放在高地址

       PC多采用小端節序,而手機多采用大端節序,在網路傳播過程中一律轉換成大端節序,所以大端節序也稱為網路位元組序。

      主要標頭檔案#include <sys/socket.h>

      linux提供了四個函式來完成主機位元組序和網路位元組序的轉換,我常用的是

      #include <netinet/in.h>

       unsigned short int htons(unsigned short int hostshort);

       TCP/IP有sockaddr_in和sockaddr_in6兩個專用socket地質結構體,用於IPv4和IPv6,個人常用sockaddr_in

        struct sockaddr_in

        {

              sa_family_t    sin_family;//地址族

              u_int16_t       sin_port;  //埠號

             struct in_addr sin_addr;  //IPv4地址結構體

         } ;

          struct in_addr

         {

            u_int32_t s_addr;     //IPv4地址,要用網路位元組序表示

         };

         ||||||||||特別注意的是所有的專用socket地址型別的變數在使用的時候都需要轉化為通用socket地址型別sockaddr,因為所有socket程式設計介面使用的地址引數的型別都是sockaddr.

         IP地址轉換函式

         對於IPv4,用的是點分十進位制字串表示,但是程式設計中我們需要先把它們轉化為整數(二進位制)方能使用.

          #include <arpa/inet.h>

          int_addr_t inet_addr(const char* strptr);//作用是把原先的點分十進位制字串轉換成網路位元組序表示的IPv4地址.

          (1) 建立socket

            #include <sys/types.h>

            #include <sys/socket.h>

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

            domain引數告訴系統使用哪個底層協議族AF_INET(OR PF_INET)表示的是IPv4

            type用於指定服務型別,SOCK_STREAM(流服務),SOCK_DGRAM(資料報)服務。取用SOCK_STREAM表示用的是TCP協議,用SOCK_DGRAM則表示用的是UDP服務。

            protocol通常設定為0 

            返回值:成功則返回一個檔案描述符,失敗就返回-1

           (2)命名socket

             #include <sys/types.h>

             #include <sys/socket.h>

             int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen);

             這個操作客戶端一般不用,他將一個socket與socket地址繫結在一起,這樣客戶端才知道如何連線他。

             bind將my_addr所指的socket地址分配給未命名的sockfd檔案描述符

             addrlen指出該socket地址的長度      

              bind成功返回0,失敗返回-1.

             (3)監聽sockfd

              sockfd被命名之後,我們還需要建立一個監聽佇列來存放待處理的客戶連線。

              #include <sys/socket.h>

               int listen(int sockfd,int backlog);

               sockfd指定被監聽的sockfd,backlog提示核心監聽佇列的最大長度。只表示處於完全連線狀態的socket的上限

              (4)接受連線

                #include <sys/types.h>

                #include <sys/socket.h>

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

                addr的作用是用來監聽遠端地址

                sockfd的作用是執行過listen系統呼叫的監聽socket。

              (5)發起連線

                 #include <sys/types.h>

                 #include <sys/socket.h>

                 int connect(int sockfd,const struct sockaddr* serv_addr,socklen_t addrlen);

                 sockfd,是由系統呼叫產生的,第二個引數是監聽的sockfd地址,addrlen指定地址長度

                 成功0,失敗-1                

               (6)關閉連線

                   #include <unistd.h>

                   int close(int fd);

                  每使用一次fd引用計數減一,知道減到0為止。

                  (7)TCP資料讀寫

                   #include <sys/types.h>

                   #include <sys/socket.h>

                   ssize_t recv(int sockfd,void *buf,size_t len,int flags);

                   ssize_t send(int sockfd,const void* buf,size_t len,int flags);

                   向sockfd上讀寫資料,最後一個引數一般都是0

#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>


int main(int argc,char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd!=-1);


    struct sockaddr_in saddr,caddr;


    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6500);
    saddr.sin_addr.s_addr = inet_addr("192.168.1.11");


    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res!=-1);


    listen(sockfd,5);




    int len = sizeof(caddr);
    int c = accept(sockfd,(struct sockaddr*)&caddr,&len);


    while(1)
    {
         if(c<0)
         {
              continue;
         }


         char buff[128] = {0};
         recv(c,buff,127,0);
         printf("%s",buff);
    }
    close(c);
    return 0;
}

              

客戶端                                   

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int main(int argc,char *argv[])
{
   int sockfd = socket(AF_INET,SOCK_STREAM,0);
   assert(sockfd!=-1);


   struct sockaddr_in saddr;//雖然操作一樣,但是客戶端是為了去連結服務端
   memset(&saddr,0,sizeof(saddr));
   saddr.sin_family = AF_INET;
   saddr.sin_port = htons(6500);//轉換成大端
   saddr.sin_addr.s_addr = inet_addr("192.168.1.11");
 //要用就必須先執行起來
   int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//連結到服務端上面
   assert(res!=-1);
   //執行到這個地方說明三次握手完成


   while(1)
   {
       printf("input:\n");
       char buff[128] = {0};


       fgets(buff,128,stdin);//fgets會用到空格回車
       if(strncmp(buff,"end",3)==0)
       {
           break;
       }


       send(sockfd,buff,strlen(buff),0);//把收到的訊息再發送出去
       memset(buff,0,128);
   }


   close(sockfd);//關閉
   return 0;
}

ACK : TCP協議規定,只有ACK=1時有效,也規定連線建立後所有傳送的報文的ACK必須為1

SYN(SYNchronization) : 在連線建立時用來同步序號。當SYN=1而ACK=0時,表明這是一個連線請求報文。對方若同意建立連線,則應在響應報文中使SYN=1和ACK=1. 因此,  SYN置1就表示這是一個連線請求或連線接受報文。

FIN (finis)即完,終結的意思, 用來釋放一個連線。當 FIN = 1 時,表明此報文段的傳送方的資料已經發送完畢,並要求釋放連線。

TCP三次握手四次揮手中三種報文的作用

首先由Client發出請求連線即 SYN=1 ACK=0  (請看頭欄位的介紹), TCP規定SYN=1時不能攜帶資料,但要消耗一個序號,因此宣告自己的序號是 seq=x

然後 Server 進行回覆確認,即 SYN=1 ACK=1 seq=y, ack=x+1,

(為什麼在連線的時候伺服器端在接收到了客戶端的SYN後返回的時候返回的是SYN=1 ACK=1...)的原因

作為補充

http://blog.csdn.net/oney139/article/details/8103223

這位前輩寫的很詳細,我做個記錄方便以後複習