10、網絡基礎和SOCKET

10、網絡基礎和SOCKET

1、TCP/IP協議概述

  • TCP/IP是互聯網的基礎
  • OSI參考模型與TCP/IP參考模型對應關係:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yqkeHjJS-1590153327819)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200520210534189.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-EMMAF4pJ-1590153327849)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200520210637207.png)]

  • TCP/IP 實際上是一個一起工作的通信家族,爲網際數據通信提供通路。爲討論方便可將
  • TCP/IP 協議組大體上分爲三部分:
    • 1.Internet 協議(IP)
    • 2.傳輸控制協議(TCP)和用戶數據報文協議(UDP)
    • 3.處於TCP 和UDP 之上的一組協議專門開發的應用程序。它們包括:TELNET,文件傳送協議(FTP),域名服務(DNS)和簡單的郵件傳送程序(SMTP)等許多協議。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BgzRURzv-1590153327852)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200520210804029.png)]

2、傳輸層協議 - TCP(傳輸控制協議)

  • 傳輸層協議。包括傳輸控制協議TCP和用戶數據報文協議UDP。
  • 傳輸控制協議(TCP)。由於IP 提供非連接型傳遞服務,因此TCP應爲應用程序存取網絡創造了條件,使用可靠的面向連接的傳輸層服務。該協議爲建立網際上用戶進程之間的對話負責。此外,還確保兩個以上進程之間的可靠通信。它所提供的功能如下:
    • 1.監聽輸入對話建立請求。
    • 2.請求另一網絡站點對話。
    • 3.可靠的發送和接收數據。
    • 4.適度的關閉對話。

三次握手

  • 1、初始化主機通過一個同步標誌置位的數據段發出會話請求。
  • 2、接收主機通過發回具有以下項目的數據段表示回覆:同步標誌置位、即將發送的數據段的起始字節的順序號、應答並帶有將收到的下一個數據段的字節順序號。
  • 3、請求主機再回送一個數據段,並帶有確認順序號和確認號。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-cQGPjwkJ-1590153327858)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200520211335682.png)]

3、傳輸層協議 - UDP(用戶數據報文協議)

  • 用戶數據報文協議(UDP)。UDP 提供不可靠的非連接型傳輸層服務,它允許在源和目的地站點之間傳送數據,而不必在傳送數據之前建立對話。此外,該協議還不使用TCP使用的端對端差錯校驗。當使用UDP時,傳輸層功能全都發揮,而開銷卻比較低。它主要用於那些不要求TCP協議的非連接型的應用程序。例如,名字服務、網絡管理、視頻點播和網絡會議等。

4、應用層協議

  • Telnet
  • 文件傳送協議(FTP 和TFTP)
  • 簡單的郵件傳送協議(SMTP)
  • 域名服務(DNS)等協議。

5、網絡編程基礎

socket概述

  • 爲了簡化開發通信程序的工作,由Berkely學校開發了一套網絡通信程序的API函數標準
  • socket標準被擴展成window socket和unix socket
  • linux中的網絡編程通過socket接口實現。Socket既是一種特殊的IO,它也是一種文件描述符。一個完整的Socket 都有一個相關描述{協議,本地地址,本地端口,遠程地址,遠程端口};每一個Socket 有一個本地的唯一Socket 號,由操作系統分配。

SOCKET分類

  • 流式套接字(SOCK_STREAM)
    • 流式的套接字可以提供可靠的、面向連接的通訊流。它使用了TCP協議。TCP 保證了數據傳輸的正確性和順序性。
  • 數據報套接字(SOCK_DGRAM)
    • 數據報套接字定義了一種無連接的服務,數據通過相互獨立的報文進行傳輸,是無序的,並且不保證可靠,無差錯。使用數據報協議UDP協議。
  • 原始套接字
    • 原始套接字允許對低層協議如IP或ICMP直接訪問,主要用於新的網絡協議實現的測試等。

套接字地址結構

struct sockaddr{
	unsigned short sa_family; /* address族, AF_xxx */
	char sa_data[14]; 	  /* 14 bytes的協議地址 */
};
//sa_family 一般來說, IPV4使用“AF_INET”。
//sa_data 包含了一些遠程電腦的地址、端口和套接字的數目,它裏面的數據是雜溶在一起的。

sockaddr_in地址結構

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一樣大小)*/
};

字節序列轉換

  • 因爲每一個機器內部對變量的字節存儲順序不同(有的系統是高位在前,底位在後,而有的系統是底位在前,高位在後 ),而網絡傳輸的數據大家是一定要統一順序的。所以對與內部字節表示順序和網絡字節順序不同的機器,就一定要對數據進行轉換。

字節轉換函數

  • htons()——“Host to Network Short”

    主機字節順序轉換爲網絡字節順序(對無符號短型進行操作2bytes)

  • htonl()——“Host to Network Long”

    主機字節順序轉換爲網絡字節順序(對無符號長型進行操作4bytes)

  • ntohs()——“Network to Host Short”

    網絡字節順序轉換爲主機字節順序(對無符號短型進行操作2bytes)

  • ntohl()——“Network to Host Long ”

    網絡字節順序轉換爲主機字節順序(對無符號長型進行操作4bytes)

地址格式轉換

  • linux提供將點分格式的地址轉於長整型數之間的轉換函數。
    • inet_addr()能夠把一個用數字和點表示IP 地址的字符串轉換成一個無符號長整型。
    • inet_ntoa()
    • inet_aton()

基本套接字調用

socket()		bind()		    connect()	 
listen()		accept()		send()	
recv()		    sendto() 		shutdown()
recvfrom()		close()		 	getsockopt()      
setsockopt()		            getpeername()
getsockname()					gethostbyname()
gethostbyaddr()					getprotobyname()
fcntl()

基於流套接字的編程流程

6、socket示例

//server.cpp

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

#define PORT 0x8888
int main(int argc,char *argv[])
{
    int sfp,nfp;
    static int number=0;
    struct sockaddr_in s_add,c_add;
    char buffer[1024]={0};
    printf("Hello,welcome to my server!\n");
    sfp=socket(AF_INET,SOCK_STREAM,0);
    if(-1==sfp){
        printf("socket fail!\n");
        return -1;
    }
    printf("socket ok!\n");

    bzero(&s_add,sizeof(struct sockaddr_in));
    s_add.sin_family=AF_INET;
    s_add.sin_addr.s_addr=htonl(INADDR_ANY);
    s_add.sin_port=htons(PORT);
    if(-1==bind(sfp,(struct sockaddr *)(&s_add),sizeof(struct sockaddr))){
        printf("bind fail!\n");
        return -1;
    }
    printf("bind ok!\n");

    if(-1==listen(sfp,5)){
        printf("listen fail!\n");
        return -1;
    }
    printf("listen ok\n");
    socklen_t addrlen = sizeof(struct sockaddr_in);

    while(1){
        nfp=accept(sfp,(struct sockaddr *)(&c_add),&addrlen);
        if(-1==nfp){
            printf("accept fail!\n");
            return -1;
        }
        printf("accept ok!\nServer start get connect from %#x :%#x\n",ntohl(c_add.sin_addr.s_addr),ntohs(c_add.sin_port));
        number++;
        int ret=fork();    
        if(ret>0){
            break;
        }
    }
    while(1){
        memset(buffer,0,sizeof(buffer));
        int len=read(nfp,buffer,sizeof(buffer));
        if(len>0){
            printf("client_%d:%s\n",number,buffer);
            write(nfp,buffer,sizeof(buffer));
        }
    }
    close(sfp);
    return 0;
}
//client.cpp

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

#define PORT 0x8888
int main(int argc,char *argv[])
{
    int cfd;
    int recbytes;
    char buffer[1024]={0};
    struct sockaddr_in s_add;
    printf("Hello,welcome to client !\n");

    cfd=socket(AF_INET,SOCK_STREAM,0);
    if(-1==cfd){
        printf("socket fail!\n");
        return -1;
    }
    printf("socket ok!\n");

    bzero(&s_add,sizeof(struct sockaddr_in));
    s_add.sin_family=AF_INET;
    s_add.sin_addr.s_addr=inet_addr("127.0.0.1");
    s_add.sin_port=htons(PORT);
    printf("s_addr=%#x,port : %#x\n",s_add.sin_addr.s_addr,s_add.sin_port);
    if(-1==connect(cfd,(struct sockaddr *)(&s_add),sizeof(struct sockaddr))){
        printf("connect fail!\n");
        return -1;
    }
    printf("connect to server success!\n");

    while(1){
        memset(buffer,0,sizeof(buffer));
        printf("please input:");
        fgets(buffer,sizeof(buffer),stdin);
        printf("\n");
        write(cfd,buffer,sizeof(buffer));
        memset(buffer,0,sizeof(buffer));
        if(-1==(recbytes=read(cfd,buffer,1024))){
            printf("read data fail!\n");
            return -1;
        }
        if(recbytes>0){
            printf("server:%s\n",buffer);
            //buffer[recbytes]='\0';
            //printf("%s\n",buffer);
        }
    }
    getchar();
    close(cfd);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章