TCP--server

一個在linux下的基於tcp的服務器和客戶端;

具體的網絡基礎參照謝希仁老師的《計算機網絡》來入門;

在TCP/IP協議中,“IP地址+TCP或UDP端⼜號”唯⼀標識⽹
絡通訊中的⼀個進程,“IP地址+端⼜號”就稱爲socket;

先說一些規定吧:

  1. TCP/IP協議規定,⽹絡數據流應採⽤⼤端字節序,即低地址⾼字節。
  2. 先發出的數據是低地址,後發出的數據是⾼地址。

所以爲了程序的移植性,系統提供了一些接口供我們調用:
hton
其中h表⽰host,n表⽰network,l表⽰32位長整數,s表⽰16位短整數,例 如htonl表⽰將32位的長整數從主機字節序轉換爲⽹絡字節序,例如將IP地址轉換後準備發送。

然後我們平時所見到的ip地址一般都是點分式,但是在系統中實際是一個32位的整形,所以系統也爲我們提供了一套轉換的接口:
把點分式字符串轉爲in_addr_t:
inet_addr
同時把in_addr_t轉回成字符串點分式的:
inet_ntoa

上篇文章中講過TCP的三次握手和四次揮手了,這次再來看其具體的通信過程及其給用戶暴露的接口:

TCP
然後我們再詳細談一下其中的接口的作用:

  1. socket()打開⼀個⽹絡通訊端⼜,如果成功的話,就像open()⼀樣返回⼀個⽂件描述符,應⽤程序可以像讀寫⽂件⼀樣⽤read/write在⽹絡上收發數據,如果socket()調⽤出錯則返回-1。對於TCP協議,type參數指定爲SOCK_STREAM,表⽰⾯向流的傳輸協議。如果是UDP協議,則type參數指定爲SOCK_DGRAM,表⽰⾯向數據報的傳輸協 議。第三個參數可以忽略,設爲0就好了;
    這裏寫圖片描述

  2. 因爲服務器的IP地址和端口號一般都是要固定不變的,所以客戶端知道服務器的IP地址就可以訪問了,所以服務器要綁定一個固定的IP地址和端口號,所以要用的bind()函數,bind()的作⽤是將參數sockfd和myaddr綁定在⼀起,使sockfd這個⽤於⽹絡通訊的⽂件描述符監聽myaddr所描述的地址和端⼜號。
    這裏寫圖片描述

  3. 這個時候我們就要監聽是不是有客戶要來連接我們了,listen()聲明sockfd處於監聽狀態,並且最多允許有backlog個客戶端處於連接等待狀態,如果接收到更多的連接請求就忽略。listen()成功返回0,失敗返回-1。
    這裏寫圖片描述

  4. 三⽅握⼿完成後,服務器調⽤accept()接受連接,如果服務器調用accept()時還沒有客戶端的連接請求,就阻塞等待直到有客戶端連接上來。cliaddr是⼀個傳出參數,accept()返回時傳出客戶端的地址和端口號。addrlen參數是⼀個傳⼊傳出參數(value-result argument),傳⼊的是調⽤者提供的緩衝區cliaddr的長度以避免緩衝區溢出問題,傳出的是客戶端地址結構體的實際長度(有可能沒有佔滿調⽤者提供的緩衝區)。如果給cliaddr 參數傳NULL,表⽰不關⼼客戶端的地址。
    accept

    這樣我們就可以的到客戶端的連接了,現在也不做多麼複雜的事情,就是把他傳來的數據讀出來,再給他寫回去就好了;

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>                                                                                                                                             
  4 #incl  ude <netinet/in.h>
  5 #include <arpa/inet.h>
  6 #include <stdlib.h>
  7 
  8 
  9 int main(int argc,char* argv[])
 10 {
 11     if(argc != 3)//judge input
 12     {              
 13         printf("tcp/_server + ip + port_number\n");
 14         exit(1);
 15     }
 16     int sock = socket(AF_INET,SOCK_STREAM,0);//creat socket
 17     if(sock < 0)
 18     {
 19         printf("creat socket error\n");
 20         exit(2);
 21     }
 22     
 23     struct sockaddr_in server_socket;//init sockaddr_in
 24     struct sockaddr_in client_socket;
 25     server_socket.sin_family = AF_INET;
 26     server_socket.sin_addr.s_addr = inet_addr(argv[1]);
 27     server_socket.sin_port = htons(atoi(argv[2]));
 28     
 29     if(bind(sock,(struct sockaddr*)&server_socket,sizeof(server_socket)) < 0)//bind sock
 30     {
 31         printf("bind error!\n");
 32         exit(3);
 33     }
 34 
 35     if(listen(sock,5) < 0)//listen sock
 36     {
 37         printf("listen error\n");
 38         exit(4);
 39     }                                                                                                                                                               
 40 
 41     while(1)
 42     {
 43         int len = 0;
 44         int client_sock = accept(sock,(struct sockaddr*)&client_socket,&len);//accept sock
 45         if(client_sock < 0)
 46         {
 47             printf("accept error!\n");
 48             continue;
 49         }
 50 
 51         printf("a new connect from %s   %d\n",inet_ntoa(client_socket.sin_addr),client_socket.sin_port);
 52         while(1)
 53         {//data processing
 54             char buf[1024];
 55             ssize_t s = read(client_sock,buf,sizeof(buf));
 56             if(s>0)
 57             {
 58                 buf[s] = 0;
 59                 printf("client :# %s\n",buf);
 60             }
 61             else
 62             {
 63                 printf("read down ..........break;");
 64                 break;
 65             }
 66             write(client_sock,buf,sizeof(buf)-1);
 67         }
 68     }
 69 
 70 
 71     return 0;
 72 }                                             

這樣一個基本的服務器就寫好了,只是最基本的,也只能支持一個人連接而已;
下篇再來講客戶端吧;

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