計算機網絡Socket編程之TCP協議

>TCP協議位於傳輸層,是一種面向鏈接的可靠的傳輸協議程序員

>socket(套接字):是IP地址與端口號的統稱編程

>套接字的基本結構服務器

          struct sockaddr   這個結構用來存儲套接字地址網絡

結構體的定義dom

  struct sockaddr {socket

  unsigned short sa_family; /* address族, AF_xxx */ide

  har sa_data[14]; /* 14 bytes的協議地址 */函數

  };工具

       sa_family    通常來講,都是「AFINET」。spa

       sa_data      包含了一些遠程電腦的地址、端口和套接字的數目,它裏面的數據是雜溶在一切的。

爲了處理struct sockaddr, 程序員創建了另一個類似的結構 struct sockaddr_in:

   struct sockaddr_in (「in」 表明 「Internet」)

   struct sockaddr_in {

   short int sin_family; /* Internet地址族 */

   unsigned short int sin_port; /* 端口號 */

   struct in_addr sin_addr; /* Internet地址 */

   unsigned char sin_zero[8]; /* 添0(和struct sockaddr同樣大小)*/

   };

這個結構提供了方便的手段來訪問socket address(struct sockaddr)結構中的每個元素


 >IP 地址轉換

Linux 系統提供和不少用於轉換IP 地址的函數.首先,假設你有一個struct sockaddr_in ina,而且你的IP 166.111.69.52 ,你想把你的IP 存儲到ina 中。你可使用的函數: inet_addr() ,它可以把一個用數字和點表示IP 地址的字符串轉換成一個無符號長整型。你能夠像下面這樣使用它:

      ina.sin_addr.s_addr = inet_addr(「166.111.69.52」);

注意:

inet_addr() 返回的地址已是網絡字節順序了,你沒有必要再去調用htonl() 函數反過來,若是你有一個struct in_addr 而且你想把它表明的IP 地址打印出來(按照數字.數字.數字.數字的格式),那麼你可使用函數inet_ntoa()(「ntoa」表明「Network to ASCII」),它會把struct in_addr 裏面存儲的網絡地址以數字.數字.數字.數字的格式。

l inet_ntoa() 使用struct in_addr 做爲一個參數,不是一個長整型值。


>套接字字節轉換程序的列表:

l htons()——「Host to Network Short」主機字節順序轉換爲網絡字節順序(對無符號短型進行操做4 bytes)

l htonl()——「Host to Network Long」 主機字節順序轉換爲網絡字節順序(對無符號長型進行操做8 bytes)

l ntohs()——「Network to Host Short 「 網絡字節順序轉換爲主機字節順序(對無符號短型進行操做4 bytes)

l ntohl()——「Network to Host Long 「 網絡字節順序轉換爲主機字節順序(對無符號長型進行操做8 bytes)


>基本套接字調用

socket() 函數

取得套接字描述符

socket 函數的定義是下面這樣子的:

#include <sys/types.h>

#include <sys/socket.h>

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


bind() 函數

bind()函數能夠幫助你指定一個套接字使用的端口。

當你使用socket() 函數獲得一個套接字描述符,你也許須要將socket 綁定上一個你的機器上的端口。當你須要進行口監聽 listen()操做,等待接受一個連入請求的時候,通常都須要通過這一步。好比網絡泥巴(MUD),Telnet a.b.c.d 4000.若是你只是想進行鏈接一臺服務器,也就是進行 connect() 操做的時候,這一步並非必須的。

bind()的系統調用聲明以下:

#include <sys/types.h>

#include <sys/socket.h>

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

參數說明:

l sockfd 是由socket()函數返回的套接字描述符。

l my_addr 是一個指向struct sockaddr 的指針,包含有關你的地址的信息:名稱、端口和IP 地址。

addrlen 能夠設置爲sizeof(struct sockaddr)。

connect()函數

讓咱們花一點時間來假設你是一個Telnet 應用程序。你的使用者命令你創建一個套接字描述符。你聽從命令,調用了socket()。而後,使用者告訴你鏈接到「166.111.69.52」的23 端口(標準的Telnet 端口)你應該怎麼作呢?

你很幸運:Telnet 應用程序,你如今正在閱讀的就是套接字的進行網絡鏈接部分:

connect()。

connect() 函數的定義是這樣的:

#include <sys/types.h>

#include <sys/socket.h>

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

connect()的三個參數意義以下:

l sockfd :套接字文件描述符,由socket()函數返回的。

l serv_addr 是一個存儲遠程計算機的IP 地址和端口信息的結構。

l addrlen 應該是sizeof(struct sockaddr)。

listen() 函數

listen()函數是等待別人鏈接,進行系統偵聽請求的函數。當有人鏈接你的時候,你有兩步須要作:經過listen()函數等待鏈接請求,而後使用accept()函數來處理。(accept()函數在下面介紹)。

listen()函數調用是很是簡單的。函數聲明以下:

#include <sys/socket.h>

int listen(int sockfd, int backlog);

listen()函數的參數意義以下:

l sockfd 是一個套接字描述符,由socket()系統調用得到。

l backlog 是未通過處理的鏈接請求隊列能夠容納的最大數目。

backlog 具體一些是什麼意思呢?每個連入請求都要進入一個連入請求隊列,等待listen 的程序調accept((accept()函數下面有介紹)函數來接受這個鏈接。當系統尚未調用accept()函數的時候,若是有不少鏈接,那麼本地可以等待的最大數目就是backlog 的數值。你能夠將其設成5 到10 之間的數值

accept()函數

函數accept()有一些難懂。當調用它的時候,大體過程是下面這樣的:

有人從很遠很遠的地方嘗試調用 connect()來鏈接你的機器上的某個端口(固然是你已經在listen()的)。他的鏈接將被 listen 加入等待隊列等待accept()函數的調用(加入等待隊列的最多數目由調用listen()函數的第二個參數backlog 來決定)。你調用 accept()函數,告訴他你準備鏈接。accept()函數將回返回一個新的套接字描述符,這個描述符就表明了這個鏈接.好,這時候你有了兩個套接字描述符,返回給你的那個就是和遠程計算機的鏈接,而第一個套接字描述符仍然在你的機器上原來的那個端口上listen()。這時候你所獲得的那個新的套接字描述符就能夠進行send()操做和recv()操做了。

下面是accept()函數的聲明:

#include <sys/socket.h>

int accept(int sockfd, void *addr, int *addrlen);

accept()函數的參數意義以下:

l sockfd 是正在listen() 的一個套接字描述符。

l addr 通常是一個指向struct sockaddr_in 結構的指針;裏面存儲着遠程鏈接過來的計算機的信息(好比遠程計算機的IP 地址和端口)


>   下面咱們就來模擬一個socket編程的TCP協議

   server.c 的做用是接受client的請求,並與client進行簡單的數據通訊,總體爲一個阻塞式的網絡聊天工具

//server.c

  1#include<stdio.h>

  2 #include<stdlib.h>

  3 #include<sys/types.h>

  4 #include<sys/socket.h>

  5 #include<netinet/in.h>

  6 #include<string.h>

  7 #include<error.h>

  8 #include<arpa/inet.h>

  9 #include<pthread.h>

 10 static void usage(const char* proc)

 11 {

 12     printf("Usage: %s [ip] [port]\n",proc);

 13 }

 14 void *thread_run(void* arg)

 15 {

 16     printf("creat a new thread\n");

 17     int fd=(int)arg;

 18     char buf[1024];

 19     while(1)

 20     {

 21         memset(buf,'\0',sizeof(buf));

 22         ssize_t _s=read(fd,buf,sizeof(buf)-1);

 23         if(_s>0)

 24         {

 25             printf("client#: %s\n",buf);

 26             write(fd,buf,strlen(buf));

 27         }

 28         else if(_s==0)

 29         {

 30             printf("client close...\n");

 31             break;

 32         }

 33         else

 34         {

 35             printf("read eror...\n");

 36            break;

 37         }

 38         return (void*)0;

 39     }

 40 }

 41 int main(int argc,char* argv[])

 42 {

 43     if(argc!=3)

 44     {

 45         usage(argv[0]);

 46         exit(1);

 47     }

 48     int listen_sock=socket(AF_INET,SOCK_STREAM,0);

 49     if(listen_sock<0)

 50     {

 51         perror("socket");

 52         exit(2);

 53     }

 54     struct sockaddr_in server_socket;

 55     server_socket.sin_family=AF_INET;

 56     server_socket.sin_addr.s_addr=inet_addr(argv[1]);

 57     server_socket.sin_port=htons(atoi(argv[2]));

 58     int opt=1;

 59     setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

 60 

 61     if(bind(listen_sock,(struct sockaddr*)&server_socket,sizeof(server_socke    t))<0)

 62     {

 63         perror("bind");

 64         exit(3);

 65     }

 66     listen(listen_sock,5);

 67     struct sockaddr_in peer;

 68     socklen_t len=sizeof(peer);

 69     int fd=accept(listen_sock,(struct sockaddr*)&peer,&len);

 70     printf("fd: %d\n",fd);

 71     if(fd<0)

 72     {

 73         perror("accept");

 74         exit(4);

 75     }

 76     printf("get a new link,socket->[%s] [%d]",inet_ntoa(peer.sin_addr),ntohs    (peer.sin_port));

 77     while(1)

 78     {

 79         pthread_t id;

 80         pthread_create(&id,NULL,thread_run,(void*)fd);

 81         pthread_join(id,NULL);

 82     }

 83     return 0;

 84 }

//client.c

1 #include<stdio.h>

  2 #include<sys/types.h>

  3 #include<sys/socket.h>

  4 #include<netinet/in.h>

  5 #include<arpa/inet.h>

  6 #include<string.h>

  7 #include<pthread.h>

  8 #include<error.h>

  9 #include<stdlib.h>

 10 static void usage(const char *proc)

 11 {   

 12     printf("usage: %s [ip] [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 sock=socket(AF_INET,SOCK_STREAM,0);

 22     if(sock==0)

 23     {

 24         perror("socket");

 25         exit(2);

 26     }

 27     struct sockaddr_in client_sock;

 28     client_sock.sin_family=AF_INET;

 29     client_sock.sin_addr.s_addr=inet_addr(argv[1]);

 30     client_sock.sin_port=htons(atoi(argv[2]));

 31 

 32     if(connect(sock,(struct sockaddr*)&client_sock,sizeof(client_sock))<0)

 33     {

 34         perror("connect");

 35         exit(3);

 36     }

 37     char buf[1024];

 38     while(1)

 39     {

 40         memset(buf,'\0',sizeof(buf));

 41         printf("Please Enter:");

 42         fflush(stdout);

 43         ssize_t _s=read(0,buf,sizeof(buf)-1);

 44         if(_s>0)

 45         {

 46             buf[_s-1]='\0';

 47             write(sock,buf,strlen(buf));

 48             _s=read(sock,buf,sizeof(buf));

 49             if(_s>0)

 50             {

 51                 printf("%s\n",buf);

 52             }

 53         }

 54     }

 55     return 0;

 56 }

運行結果:

wKiom1enIHriwMqeAAEvXehvih0770.png-wh_50

相關文章
相關標籤/搜索