1. 程式人生 > >套接字程式設計(一)----基於TCP協議

套接字程式設計(一)----基於TCP協議

套接字(socket):可以看做是不同主機之間的程序進行雙向通訊的端點,即通訊的兩方的一種約定,用套接字中的相關函式來完成通訊過程。

**socket=Ip Address+TCP/UDP+port

三次握手建立連線
這裡寫圖片描述
當客戶端呼叫connect時,觸發了連線請求,向伺服器傳送了SYN J包,這時connect進入阻塞狀態;伺服器監聽到連線請求,即收到SYN J包,呼叫accept函式接收請求向客戶端傳送SYN K ,ACK J+1,這時accept進入阻塞狀態;客戶端收到伺服器的SYN K ,ACK J+1之後,這時connect返回,並對SYN K進行確認;伺服器收到ACK K+1時,accept返回,至此三次握手完畢,連線建立。

四次揮手釋放連線
這裡寫圖片描述
1、某個應用程序首先呼叫close主動關閉連線,這時TCP傳送一個FIN M;
2、另一端接收到FIN M之後,執行被動關閉,對這個FIN進行確認。它的接收也作為檔案結束符傳遞給應用程序,因為FIN的接收意味著應用程序在相應的連線上再也接收不到額外資料;
3、一段時間之後,接收到檔案結束符的應用程序呼叫close關閉它的socket。這導致它的TCP也傳送一個FIN N;
4、接收到這個FIN的源傳送端TCP對它進行確認。

以下庫函式做⽹網路位元組序和主機位元組序的轉換。
這裡寫圖片描述
h—-host主機
n—-net網路
l —-32位長整數
s —-16位短整數
例 :htonl表示將32位的長整數從主機位元組序轉換為網路位元組序,例如將IP地址轉換後準備傳送。如果 主機是小端位元組序,這些函式將引數做相應的大小端轉換然後返回,如果主機是大端位元組序,這些函式不做轉換,將引數原封不動地返回。

1、建立socket
這裡寫圖片描述
domain:協議域,協議域決定了socket的地址型別,在通訊中必須採用對應的地址,AF_INET決定了要用ipv4地址(32位的)與埠號(16位的)的組合如下:
這裡寫圖片描述
type:指socket型別。流式Socket(SOCK_STREAM)是一種面向連線的Socket,針對於面向連線的TCP服務應用。資料報式Socket(SOCK_DGRAM)是一種無連線的Socket,對應於無連線的UDP服務應用。。
這裡寫圖片描述
protocal:一般設為0,會自動選擇第二個引數型別對應的預設協議。

2、繫結bind
socket:一個套接字描述符。
address:一個sockaddr結構指標,該結構中包含了要結合的地址和埠號。
這裡寫圖片描述


address_len:確定address緩衝區的長度。
這裡寫圖片描述
3、listen監聽,第二個引數為最大連線數,一般設為10。
這裡寫圖片描述
4、accept函式在套介面接受連線
這裡寫圖片描述
5、connect函式,用於建立與指定外部埠的連線,
這裡寫圖片描述
伺服器端:

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int startup(const char *_ip,int _port)
{
    //create socket
  int sock=socket(AF_INET,SOCK_STREAM,0);
  if(sock<0)
  {
    perror("socket");
    return 2;
  }
  //bind
  struct sockaddr_in local;
  local.sin_family=AF_INET;
  local.sin_port=htons(_port);
  local.sin_addr.s_addr=inet_addr(_ip);
  if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
  {
    perror("bind");
    return 3;
  }
  //listen
  if(listen(sock,10)<0)
  {
    perror("listen");
    return 4;
  }
  return sock;
}
static void usage(const char *proc)
{
  printf("usage:[ip] [port]\n",proc);
}
int main(int argc,char *argv[])
{
    if(argc!=3)
    {
      usage(argv[0]);
      return 1;
    }
    int listen_sock=startup(argv[1],atoi(argv[2]));
    struct sockaddr_in remote;
    socklen_t len=sizeof(remote);
    char buf[1024];
    while(1)
    {
     int sock=accept(listen_sock,(struct sockaddr*)&remote,&len);
     if(sock<0)
     {
       perror("accept");
       continue;
     }
     printf("client ip: %s, port: %d\n",inet_ntoa(remote.sin_addr),ntohs(remote.sin_port));
     while(1)
     {
      ssize_t s=read(sock,buf,sizeof(buf)-1);
      if(s>0)
      {
        buf[s]=0;
        printf("client say:%s\n",buf);
        write(sock,buf,strlen(buf));
      }
     }
    }
  return 0;
}

客戶端



#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
static void usage(const char *proc)
{
  printf("%s [server_ip] [server_port]\n",proc);
}
int main(int argc,char *argv[])
{
    if(argc!=3)
    {
     usage(argv[0]);
     return 1;
    }
    //create socket
  int sock=socket(AF_INET,SOCK_STREAM,0);
  if(sock<0)
  {
    perror("socket");
    return 2;
  }
  //connect
  struct sockaddr_in peer;
  peer.sin_family=AF_INET;
  peer.sin_port=htons(atoi(argv[2]));
  peer.sin_addr.s_addr=inet_addr(argv[1]);
  if(connect(sock,(struct sockaddr*)&peer,sizeof(peer))<0)
  {
   perror("connect");
   return 3;
  }
  char buf[1024];
  while(1)
  {
      printf("please enter:");
      fflush(stdout);
   ssize_t s=read(0,buf,sizeof(buf)-1);
   if(s>0)
   {
    buf[s-1]=0;
    write(sock,buf,strlen(buf));
    ssize_t _s=read(sock,buf,sizeof(buf)-1);
    if(_s>0)
    {
      buf[_s]=0;
      printf("server echo:%s\n",buf);
    }
   }
  }
  close(sock);
  return 0;
}