主頁 > 軟體設計 > ??怒肝三萬字,史詩的保姆網路編程教學??

??怒肝三萬字,史詩的保姆網路編程教學??

2021-09-27 08:57:51 軟體設計

網路編程大雜燴

  • 網路編程概念
    • 1.位元組序
    • 2.位元組序轉換
    • 3.網路位元組序---主機位元組序
    • 4.地址轉換函式
  • UDP編程
    • 1.概念
    • UDP編程核心代碼介紹
      • 發送資料—sendto 函式
      • bind函式
      • recvform函式介紹
    • 2.UDP簡單客戶端代碼演示
    • UDP簡單服務器代碼演示
    • UDP廣播功能的實作
    • UDP多播功能的實作
    • TFTP協議介紹
      • 1.1TFTP 概述
      • .1.2 TFTP 通信程序
      • 1.3TFTP 協議分析
      • 1.4客戶端檔案下載簡單流程
      • 1.5客戶端上傳檔案簡單流程
      • 1.6帶選項的TFTP檔案
      • TFTP 通信程序總結(帶選項)
      • TFTP 的下載程序的代碼演示(不帶選項)
      • TFTP 的上傳程序的代碼演示(不帶選項)
      • TFTP 的下載程序的代碼演示(帶選項)
      • TFTP 的上傳程序的代碼演示(帶選項)
  • TCP 網路編程
    • socket函式(客戶端和服務端必用)
    • bind函式(服務端專屬)
    • listen函式(服務端專屬)
    • accept 函式(服務端專屬)
    • connect 函式(客戶端專屬)
    • send函式(客戶端服務端可用)
    • recv函式(客戶端服務端可用)
    • close 關閉套接字
    • 埠復用
    • TCP簡單客戶端的撰寫
    • TCP簡單服務器的撰寫(只能同時跟一個客戶端交流)
    • TCP三次握手
    • TCP四次揮手
    • 高并發服務器的撰寫(行程版)
    • 高并發服務器的撰寫(執行緒版)

網路編程概念

網路通信要解決的是不同主機行程間的通信
1、首要問題是網路間行程標識問題
2、以及多重協議的識別問題,其網路程 序編程開發介面為 socket 隨著 UNIX 以及類 UNIX 作業系統的廣泛應用, socket 成為最流行的網路程式開發介面
socket 作用
提供不同主機上的行程之間的通信
socket 特點
1、socket 也稱“套接字”
2、是一種檔案描述符,代表了一個通信管道的一個端點
3、類似對檔案的操作一樣,可以使用 read、write、close 等函式對 socket 套接字進行網路資料的收取和發 送等操作
4、得到 socket 套接字(描述符)的方法呼叫 socket()

1.位元組序

概念:是指多位元組資料的存盤順序
分類
1、大端(將高位位元組資料存盤在低地址)
2:小端(將低位位元組資料存盤在低地址)

2.位元組序轉換

主機位元組序轉網路位元組序(網路位元組序默認大端,而主機大部分為小端)
uint32_t htonl(uint32_t hostint32)//
uint16_t htons(uint16_t hostint16);

3.網路位元組序—主機位元組序

uint32_t ntohl(uint32_t netint32);
uint16_t ntohs(uint16_t netint16);

1、網路位元組序一直是大端,且主機位元組序轉網路位元組序就是轉 大端保存,網路位元組序轉主機位元組序就是大端轉大端/小端,
2、只有在多位元組資料處理時才需要考慮位元組序,
3、同一主機間網路行程通信,不需要位元組序轉換,
4、不同主機間網路行程通信,需要位元組序轉換,

4.地址轉換函式

點分十進制數轉整數
int inet_pton(int family,const char *strptr, void *addrptr);
引數
1、協議族AF_INET
2、點分十進制串
3、轉之后的整數
回傳值:1成功
32位無符號整數轉換成點分十進制數串
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
引數
1、協議族AF_INET
232位無符號整數
3、點分十進制串
4、點分十進制串緩沖區的長度(16)
回傳值:成功:則回傳字串的首地址 失敗:回傳 NULL

UDP編程

1.概念

面向無連接的用戶資料報協議,在傳輸資料前不需要先建立連接;目地主機的運輸層收到 UDP 報文后,不需 要給出任何確認
特點:
1、相比 TCP 速度稍快些
2、簡單的請求/應答應用程式可以使用 UDP
3、對于海量資料傳輸不應該使用 UDP
4、廣播和多播應用必須使用 UDP
應用:DNS(域名決議)、NFS(網路檔案系統)、RTP(流媒體)等

在這里插入圖片描述
在這里插入圖片描述
- UDP的通信程序

UDP編程核心代碼介紹

在這里插入圖片描述

發送資料—sendto 函式

 發送資料—sendto 函式
ssize_t sendto(int sockfd,const void *buf,
 size_t nbytes,int flags,
 const struct sockaddr *to, 
 socklen_t addrlen);
功能: 向 to 結構體指標中指定的 ip,發送 UDP 資料
引數: 
sockfd:套接字
buf: 發送資料緩沖區
nbytes: 發送資料緩沖區的大小
flags:一般為 0
to:指向目的主機地址結構體的指標
addrlen:to 所指向內容的長度
注意: 
通過 to 和 addrlen 確定目的地址
可以發送 0 長度的 UDP 資料包
回傳值:

bind函式

UDP 網路程式想要收取資料需什么條件?
確定的 ip 地址
確定的 port
怎樣完成上面的條件呢?
接收端 使用 bind 函式,來完成地址結構與 socket 套接字的系結,這樣 ip、port 就固定了
發送端 在 sendto 函式中指定接收端的 ip、port,就可以發送資料了
int bind(int sockfd,
const struct sockaddr *myaddr,socklen_t addrlen);
功能: 將本地協議地址與 sockfd 系結
引數: 
sockfd: socket 套接字
myaddr: 指向特定協議的地址結構指標
addrlen:該地址結構的長度
回傳值: 
成功:回傳 0
失敗:其他

recvform函式介紹

接收資料—recvfrom 函式
ssize_t recvfrom(int sockfd, void *buf,
size_t nbytes,int flags,
 struct sockaddr *from, 
 socklen_t *addrlen);
功能: 
接收 UDP 資料,并將源地址資訊保存在 from 指向的結構中
引數: 
sockfd: 套接字
buf:接收資料緩沖區
nbytes:接收資料緩沖區的大小
flags: 套接字標志(常為 0)
from: 源地址結構體指標,用來保存資料的來源
addrlen: from 所指內容的長度
注意: 
通過 from 和 addrlen 引數存放資料來源資訊
from 和 addrlen 可以為 NULL, 表示不保存資料來源
回傳值: 
成功:接收到的字符數
失敗: -1

2.UDP簡單客戶端代碼演示

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc ,char **argv)
{
    //創建一個套接字,本質上是一個檔案描述符
     struct sockaddr_in det;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("sockfd = %d\n", sockfd);

    //指定發送給誰,系結目的主機
    det.sin_family = AF_INET;
    det.sin_port = htons(8888);
    inet_pton(AF_INET, argv[1],&det.sin_addr);
    memset(det.sin_zero, 0, 8);
    printf("%u,%u\n", det.sin_addr, det.sin_port);
    // int err = bind(sockfd, (struct sockadd *)&det, sizeof(det));
    // if(err < 0)
    // {
    //     printf("bind error\n");
    // }
    while(1)
    {
        int fd = fork();
        if(fd == 0)
        {
            char buf[50];
            socklen_t form_S = sizeof(det);
            recvfrom(sockfd, buf, sizeof(buf),0,(struct sockaddr*)&det,&form_S);
            printf("from server:%s\n",buf);
        }
        else
        {
            char *buf = (char *)malloc(20);
            fgets(buf, 20, stdin);
            *(buf + strlen(buf) - 1) = '\0';
            int a = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&det, sizeof(det));
            printf("send num:%d\n", a);

            if (strcmp(buf, "bye") == 0)
            {
                break;
            }
        }
        
    
    }
}

UDP簡單服務器代碼演示

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc ,char **argv)
{
    int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    int pipe_fd[2];
    pipe(pipe_fd);
    struct sockaddr_in my_addr;
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(10086);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    int err = bind(sock_fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
    if(err == -1)
    {
        printf("bind error\n");
    }

    while(1)
    {
        int fd = fork();
        if(fd ==0)
        {
            char buf[20];
            unsigned int client_ip;
            char from_ip[16]=" ";
            struct sockaddr_in client_addr;
            socklen_t  addrlen=sizeof(client_addr);
            client_addr.sin_family = AF_INET;
            client_addr.sin_port = htons(10086);
            read(pipe_fd[0],  (void *)&client_addr.sin_addr, sizeof( client_addr.sin_addr));
           // inet_pton(AF_INET, "10.36.145.41",&client_addr.sin_addr);
            printf("recevie :%s\n",from_ip);
            while(1)
            {
                memset(buf, 0, sizeof(buf));
                fgets(buf, sizeof(buf), stdin);
                int err = sendto(sock_fd, buf, strlen(buf), 0,(struct sockaddr *)&client_addr, addrlen);
                printf("發送的資料%d\n", err);
            }
        }
        else
        {
        char buf[20];
        char from_ip[16]=" ";
        struct sockaddr_in from_addr;
        bzero(&from_addr,sizeof(from_addr));
        memset(buf, 0, sizeof(buf));
        memset(from_ip, 0, sizeof(from_ip));
        socklen_t  addrlen=sizeof(from_addr);

        //接收到源ip
        recvfrom(sock_fd, buf, 20, 0, (struct sockaddr *)&from_addr,&addrlen);
        write(pipe_fd[1], (void *)&from_addr.sin_addr.s_addr, sizeof(from_addr.sin_addr.s_addr));
        //決議源ip的地址
        inet_ntop(AF_INET, (const void *)&from_addr.sin_addr.s_addr, from_ip,(socklen_t)16);
        printf("receive from :%s\n", from_ip);
        printf("buf :%s\n", buf);
       // sendto(sock_fd, buf, sizeof(buf), 0, (struct sockaddr *)&from_addr, addrlen);
        }
    }

}

UDP廣播功能的實作

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */

/*這是一個廣播的程式,如何向局域網進行全體主機的廣播,僅限于用在局域網
,城域網和廣域網不適用,因為他們太多主機ip了    
*/
#if 0
int main(int argc,char**argv)
{
    struct sockaddr_in det;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("sockfd = %d\n", sockfd);

    //更改mac地址為全f,修改套接字允許發送廣播的訊息
    int yes = 1;
     setsockopt(sockfd, SOL_SOCKET,SO_BROADCAST, &yes, sizeof(yes));

    //指定廣播
    det.sin_family = AF_INET;
    det.sin_port = htons(8080);
    socklen_t addrlen = sizeof(det);
    inet_pton(AF_INET, argv[1],&det.sin_addr);
    memset(det.sin_zero, 0, 8);
    printf("%u,%u\n", det.sin_addr.s_addr, det.sin_port);

    

    
    while(1)
    {
        char buf[64] = " ";
        fgets(buf, sizeof(buf), stdin);
        sendto(sockfd, buf,sizeof(buf), 0, (const struct sockaddr *)&det, addrlen);
    }
}
#endif

UDP多播功能的實作


/*
這是一個udp多播的最基本的演示的程式
*/
int main(int argc,char**argv)
{
    struct sockaddr_in det;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    printf("sockfd = %d\n", sockfd);

    char group[INET_ADDRSTRLEN] = "224.0.1.1";

    //定義一個多播地址
    struct ip_mreq mrep;

    //添加一個多播組IP
    mrep.imr_multiaddr.s_addr = inet_addr(group);

    //添加一個將要添加到多播組的ip
    mrep.imr_interface.s_addr = htons(INADDR_ANY);

    setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mrep, sizeof(mrep));

    //賦值
    det.sin_family = AF_INET;
    det.sin_port = htons(8080);

    /*轉換過來就是0.0.0.0,泛指本機的意思
    ,也就是表示本機的所有IP,因為有些機子不止一塊網卡,多網卡的情況下,
    這個就表示所有網卡ip地址的意思,
    比如一臺電腦有3塊網卡,分別連接三個網路,
    那么這臺電腦就有3個ip地址了,如果某個應用程式需要監聽某個埠,那他要監聽哪個網卡地址的埠呢?*/
    det.sin_addr.s_addr = htons(INADDR_ANY);//添加自己本地的ip地址
    socklen_t addrlen = sizeof(det);   
    memset(det.sin_zero, 0, 8);

    //系結
    bind(sockfd, (const struct sockaddr *)&det, addrlen);

    while(1)
    {
        char buf[64] = "";
        recvfrom(sockfd, buf, sizeof(buf), 0, NULL,NULL);
        printf("rece:%s\n", buf);
    }
}
/*
多播實驗總結
1.要先把當前ip加入到多播組的地址,然后客戶端發送訊息的時候會發送給 224.0.1.1,這個時候
會把訊息發送給所有加入到多播組ip,如果不想了還可以把ip地址移出多播組
*/

TFTP協議介紹

1.1TFTP 概述

TFTP:簡單檔案傳送協議
最初用于引導無盤系統,被設計用來傳輸小檔案

特點:
基于 UDP 協議實作
不進行用戶有效性認證

資料傳輸模式:
octet:二進制模式
netascii:文本模式
mail:已經不再支持

.1.2 TFTP 通信程序

在這里插入圖片描述

  1. .服務器從固定埠69接收客戶端得讀寫請求
  2. 接收到請求后服務器使用一個臨時埠發送資料包給客戶端,固定為512個位元組,也可以改固定位元組的長度
  3. 客戶端接收到資料后發送一個ack說我已經收到了
  4. 如果服務器發送的資料包小于512個位元組,那么代表服務器發送資料已經發完了,結束傳輸

1.3TFTP 協議分析

在這里插入圖片描述

1.4客戶端檔案下載簡單流程

  1. 創建udp套接字
  2. 組讀請求的資料包(0,1,“檔案名”,0,“模式”,0),發送給服務器
  3. 回圈接收資料,資料包buf長度<516,退出,否則繼續
  4. 查看資料包buf[1],如果buf[1] == 3 ;寫檔案,回復ack;buf[1] == 5;break;

1.5客戶端上傳檔案簡單流程

  1. 創建套接字
  2. 組寫請求的資料包(0,1,“檔案名”,0,“模式”,0)
  3. 客戶端打開要上傳的檔案
  4. 回圈發送資料buf,如果讀本地檔案長度<512,break;

1.6帶選項的TFTP檔案

tsize 選項
當讀操作時,tsize 選項的引數必須為“0”,服務器會回傳待讀取的檔案的大小
當寫操作時,tsize 選項引數應為待寫入檔案的大小,服務器會回顯該選項
blksize 選項
修改傳輸檔案時使用的資料塊的大小(范圍:8~65464)
timeout 選項
修改默認的資料傳輸超時時間(單位:秒)

TFTP 通信程序總結(帶選項)

1、可以通過發送帶選項的讀/寫請求發送給 server
2、如果 server 允許修改選項則發送選項修改確認包
3、server 發送的資料、選項修改確認包都是臨時 port
4、server 通過 timeout 來對丟失資料包的重新發送

TFTP 的下載程序的代碼演示(不帶選項)

我們來演示一下如何從服務器下載自己想要的檔案存進里面
實作的是TFTP的客戶端下載與上傳功能,
TFTP軟體下載鏈接
我們要和這個軟體配套做實驗

#include <stdio.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
	//創建套接字
	int sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0){
		printf("創建失敗\n");
		return 0;
		}
	else{
		printf("創建成功 %d\n",sockfd);
	}
	//給服務器發送下載請求
	struct sockaddr_in ser_addr;
	bzero(&ser_addr,sizeof(ser_addr));
	ser_addr.sin_family=AF_INET;
	ser_addr.sin_port=htons(69);
	inet_pton(AF_INET,"10.36.145.220",(void *)&ser_addr.sin_addr);
	char buf[64]="";
	int buf_len=sprintf(buf,"%c%c%s%c%s%c",0,1,"a.txt",0,"netascii",0);
	sendto(sockfd,buf,buf_len,0,(struct sockaddr *)&ser_addr,sizeof(ser_addr));
	//打開本地檔案用來接收服務器資料
	int fd = open("a.txt",O_RDWR|O_CREAT,0666);
	if(fd<0){
		perror("open");
		return 0;
		}
	
	//接收資料包
	char rcv_buf[1024]="";
	int rcv_buf_len=0;
	struct sockaddr_in from_addr;
	socklen_t addrlen=sizeof(from_addr);
	while(1){
		//接收資料函式
		bzero(&from_addr,sizeof(from_addr));
		rcv_buf_len=	recvfrom(sockfd, rcv_buf, sizeof(rcv_buf),0,(struct sockaddr*)&from_addr,&addrlen);
		if(rcv_buf[1]==5)//差錯
		{
			printf("error:%d-%d%s\n",rcv_buf[2],rcv_buf[3],rcv_buf+4);
			break;
		}
		else if(rcv_buf[1]==3)//資料包
		{
			//寫檔案
			write(fd,rcv_buf+4,rcv_buf_len-4);
			//回復ACK
			rcv_buf[1]=4;
			sendto(sockfd,rcv_buf,4,0,(struct sockaddr *)&from_addr,sizeof(from_addr));
			if(rcv_buf_len<516)
				break;
		}
		else;
	}
	
	close(sockfd);
	close(fd);
	
	return 0;
}

TFTP 的上傳程序的代碼演示(不帶選項)

#include <stdio.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
	//創建套接字
	int sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0){
		printf("創建失敗\n");
		return 0;
		}
	else{
		printf("創建成功 %d\n",sockfd);
	}
	//給服務器發送上傳請求
	struct sockaddr_in ser_addr;
	bzero(&ser_addr,sizeof(ser_addr));
	ser_addr.sin_family=AF_INET;
	ser_addr.sin_port=htons(69);
	inet_pton(AF_INET,"10.36.145.220",(void *)&ser_addr.sin_addr);
	char buf[64]="";
	int buf_len=sprintf(buf,"%c%c%s%c%s%c",0,2,"a.txt",0,"netascii",0);
	sendto(sockfd,buf,buf_len,0,(struct sockaddr *)&ser_addr,sizeof(ser_addr));
	//打開本地檔案
	int fd = open("a.txt",O_RDWR);
	if(fd<0){
		perror("open");
		return 0;
		}
	
	//接收資料包
	char rcv_buf[1024]="";
	int rcv_buf_len=0;
	unsigned short p_num=0;
	struct sockaddr_in from_addr;//用來保存服務器ip和服務器的臨時埠
	socklen_t addrlen=sizeof(from_addr);
	while(1){
		//接收資料函式
		bzero(&from_addr,sizeof(from_addr));
		recvfrom(sockfd, rcv_buf, sizeof(rcv_buf),0,(struct sockaddr*)&from_addr,&addrlen);
		if(rcv_buf[1]==5)//差錯
		{
			printf("error:%s\n",rcv_buf+4);
			break;
		}
		else if(rcv_buf[1]==4)//ACK   0 4 0 0-------0 3 0 1 512byte
		{
			//讀本地檔案
			rcv_buf_len=	read(fd,rcv_buf+4,512);// 0 4 0 0-------0 4 0 0 512byte
			rcv_buf[1]=3;// 0 4 0 0-------0 3 0 0 512byte
			p_num=ntohs(*(unsigned short *)(rcv_buf+2));//取rcv_buf的3~4位元組并保存到主機
			*(unsigned short *)(rcv_buf+2)=htons(p_num+1);
			//將ack包的編號+1,賦值給資料包0 4 0 x-------0 3 0 x+1 512byte
			printf("即將發送資料包編號:%d\n",p_num+1);
			sendto(sockfd,rcv_buf,rcv_buf_len+4,0,(struct sockaddr *)&from_addr,sizeof(from_addr));
			if(rcv_buf_len<512)
				break;
		}
		else;
	}
	
	close(sockfd);
	close(fd);
	
	return 0;
}

TFTP 的下載程序的代碼演示(帶選項)

帶選項的好處是可以修改每次發送的資料包的大小,ack的超時時間或者是其他的資訊,

#include <stdio.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
	//創建套接字
	int sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0){
		printf("創建失敗\n");
		return 0;
		}
	else{
		printf("創建成功 %d\n",sockfd);
	}
	//給服務器發送下載請求
	struct sockaddr_in ser_addr;
	bzero(&ser_addr,sizeof(ser_addr));
	ser_addr.sin_family=AF_INET;
	ser_addr.sin_port=htons(69);
	inet_pton(AF_INET,"10.36.145.220",(void *)&ser_addr.sin_addr);
	char buf[64]="";
	int buf_len=sprintf(buf,"%c%c%s%c%s%c%s%c%s%c",0,1,"a.txt",0,"netascii",0,"blksize",0,"1024",0);//發送帶選項請求
	sendto(sockfd,buf,buf_len,0,(struct sockaddr *)&ser_addr,sizeof(ser_addr));
	
	
	
	//打開本地檔案用來接收服務器資料
	int fd = open("a.txt",O_RDWR|O_CREAT,0666);
	if(fd<0){
		perror("open");
		return 0;
		}
	
	//接收資料包
	char rcv_buf[1056]="";
	int rcv_buf_len=0;
	int len=0;
	unsigned short p_num=0;
	struct sockaddr_in from_addr;
	socklen_t addrlen=sizeof(from_addr);
	
	
	//接受OACK
	
	recvfrom(sockfd, rcv_buf, sizeof(rcv_buf),0,(struct sockaddr*)&from_addr,&addrlen);
	if(rcv_buf[1]==6)//0 6 選項 0 值 0 
	{
		rcv_buf[1]=4;//0 4 選項 0 值 0 
		rcv_buf[2]=0;//0 4 0 0 值 0 
		rcv_buf[3]=0;//0 4 0 0 值 0 
		sendto(sockfd,rcv_buf,4,0,(struct sockaddr *)&from_addr,sizeof(from_addr));
	}
	else{
		return 0;
	}
	
	while(1){
		//接收資料函式
		bzero(&from_addr,sizeof(from_addr));
		rcv_buf_len=	recvfrom(sockfd, rcv_buf, sizeof(rcv_buf),0,(struct sockaddr*)&from_addr,&addrlen);
		if(rcv_buf[1]==5)//差錯
		{
			printf("error:%s\n",rcv_buf+4);
			break;
		}
		else if(rcv_buf[1]==3)//資料包
		{
			//寫檔案
			len=write(fd,rcv_buf+4,rcv_buf_len-4);
			//列印資料包編號
			p_num=ntohs(*(unsigned short * )(rcv_buf+2));
			printf("資料包編號:%d   --- %d\n",p_num,len);
			//回復ACK
			rcv_buf[1]=4;
			sendto(sockfd,rcv_buf,4,0,(struct sockaddr *)&from_addr,sizeof(from_addr));
			if(rcv_buf_len<1028)
				break;
		}
		else;
	}
	
	close(sockfd);
	close(fd);
	
	return 0;
}

TFTP 的上傳程序的代碼演示(帶選項)

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>


//客戶端有選項的上傳
#if 1
      int main(int argc,char**argv)
    {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    int p_num;
    char buf[64] ;
    char rece_buf[1056] =" ";
    printf("sockfd = %d\n", sockfd);
    struct sockaddr_in det;
    struct sockaddr_in server;


    //判斷一下 執行的時候是否攜帶了引數
    if(argc<2)
    {
        printf("你還沒有指定服務器的ip地址\n");
        return 0 ;
    }

    //指定發送給誰,系結目的主機
      bzero(&det,sizeof(det));
    det.sin_family = AF_INET;
    det.sin_port = htons(69);
    inet_pton(AF_INET, argv[1],&det.sin_addr);
    memset(det.sin_zero, 0, 8);
    printf("%u,%u\n", det.sin_addr.s_addr, det.sin_port);
    socklen_t addrlen = sizeof(det);



    //創建一個接收從服務器下載的檔案
   

          //組裝資料包并進行發送
            int buflen = sprintf(buf,"%c%c%s%c%s%c%s%c%s%c",0,2,"up.txt",0,"netascii",0,"blksize",0,"1024",0);//資料包的大小要對,不能傳多了 
            int sendnum = sendto(sockfd, buf,buflen, 0, (const struct sockaddr *)&det, addrlen);
                printf("send num %d \n",sendnum);
            int fd = open("a.txt", O_RDWR | O_CREAT, 0666);
            if(fd<1)
            {
                printf("open error \n");
                return 0 ;
            }

            //等待服務器osak的應答
              char oask_buf[1056]="";
            int rece_osk_num = recvfrom(sockfd, oask_buf, sizeof(oask_buf), 0, ( struct sockaddr *)&server, &addrlen);
          if(oask_buf[1]!=6)
          {
              printf("應答失敗\n");
              return 0;
          }
  
    while(1)
        {
                //讀取資料
               int recebuf_len =  read(fd, rece_buf + 4, 1028);

                 //把操作碼變成3,表示向服務器發送應答命令 0 0 0 0
                rece_buf[1] = 3;
                 p_num = ntohs(*(unsigned short *)(rece_buf + 2));
                 *(unsigned short *)(rece_buf + 2) = htons(p_num + 1);
                 printf("即將發送的編號:%d\n", p_num + 1);
                sendto(sockfd, rece_buf,recebuf_len+4, 0,(const struct sockaddr *)&server, addrlen);//發送的時候要使用目的ip和源埠,因為使用的是臨時埠,所以要小心

          
            //判斷接收的位元組是否小于1028,如果小于則結束
                        if(recebuf_len<1028)
                    {

                        printf("檔案傳輸完成\n");
                        break;
                    }
        // memset(oask_buf, 0, sizeof(oask_buf));
                  
                    // recvfrom(sockfd, oask_buf, sizeof(oask_buf), 0, (struct sockaddr *)&server, &addrlen);
                    // if (oask_buf[1] == 5)
                    // {
                    //     printf("error :%s\n", oask_buf + 4);
                    //  }
                    // else if(oask_buf[1] == 4)
                    // {
                    //     printf("服務器應答\n");

                    // }
            
            
        }

    close(sockfd);
	close(fd);
    }  
#endif

TCP 網路編程

TCP特點:

1、面向連接的流式協議;可靠、出錯重傳、且每收到一個資料都要給出相應的確認
2、通信之前需要建立鏈接
3、服務器被動鏈接,客戶端是主動鏈接
在這里插入圖片描述
TCP客戶端與服務端流程
在這里插入圖片描述

socket函式(客戶端和服務端必用)

iint socket(int family,int type,int protocol); 
功能:創建一個用于網路通信的 socket 套接字(描述符)
 引數:
 	family:協議族(AF_INET、AF_INET6、PF_PACKET 等),通常使用AF_INET,針對internet的
    type:套接字類(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW 等)
    	SOCK_STREAM:代表TCP協議
    	SOCK_DGRAM :表示UDP協議
    protocol:協議類別(0、IPPROTO_TCP、IPPROTO_UDP 等 ,通常使用0來代替
    回傳值:成功回傳檔案描述符,失敗回傳-1
     特點:創建套接字時,系統不會分配埠 創建的套接字默認屬性是主動的,即主動發起服務的請求;當作為服務器時,往往需要修改為被動的 
    頭檔案:#include <sys/socket.h>

bind函式(服務端專屬)

int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
功能:從函式用于將地址系結到一個套接字,
引數:
	sockfd是由socket函式呼叫回傳的檔案描述符,
	my_addr是一個指向sockaddr的指標,
	addrlen是sockaddr結構的長度,
 回傳值:失敗回傳-1

sockaddr的定義:
當呼叫編程介面函式,且該函式需要傳入地址結構時需要用 struct sockaddr 進行強制轉換

struct sockaddr{
unisgned short  as_family;
char sa_data[14];
};

sockaddr_in的定義
在定義源地址和目的地址結構的時候,選用 struct sockaddr_in;

struct sockaddr_in{
unsigned short   sin_family;     
unsigned short     sin_port;
struct in_addr   sin_addr;
unsigned char   sin_zero[8];
}
	引數:
Internet所以sin_family一般為AF_INET,
sin_port:監聽的埠號
sin_addr:通常設定為INADDR_ANY表示可以和任何的主機通信
sin_zero[8]:通常不理它

listen函式(服務端專屬)

#include <sys/socket.h>
int listen(int sockfd, int backlog); 
功能:將套接字由主動修改為被動 使作業系統為該套接字設定一個連接佇列,用來記錄所有連接到該套接字的連接
 引數:
 sockfd: socket 監聽套接字 
 backlog:連接佇列的長度 
 回傳值:成功回傳 0 失敗回傳-1

accept 函式(服務端專屬)

int accept(int sockfd,struct sockaddr *cliaddr, socklen_t *addrlen); 
功能:從已連接佇列中取出一個已經建立的連接,如果沒有任何連接可用,則進入睡眠等待(阻塞) 
引數:
sockfd: socket 監聽套接字
cliaddr: 用于存放客戶端套接字地址結構,用來給客戶端程式自動填寫,服務器端只用傳遞指標就行,
addrlen:套接字地址結構體長度的地址
 回傳值:已連接套接字
 頭檔案:#include <sys/socket.h> 
 注意:回傳的是一個已連接套接字,這個套接字代表當前這個連接

connect 函式(客戶端專屬)

int connect(int sockfd, const struct sockaddr *addr, socklen_t len);
 功能:主動跟服務器建立鏈接
  引數:
  sockfd:socket 套接字 
  addr: 連接的服務器地址結構 
  len: 地址結構體長度 
  回傳值:成功:0 失敗:其他
  注意:
  1、connect 建立連接之后不會產生新的套接字 
  2、連接成功后才可以開始傳輸 TCP 資料 
  3、頭檔案:#include <sys/socket.h>

send函式(客戶端服務端可用)

ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);
 功能:用于發送資料 
 引數:
 sockfd: 已建立連接的套接字 
 buf: 發送資料的地址 
 nbytes: 發送緩資料的大小(以位元組為單位) 
 flags: 套接字標志(常為 0)
  回傳值:成功發送的位元組數
  頭檔案:#include <sys/socket.h> 注意:不能用 TCP 協議發送 0 長度的資料包

recv函式(客戶端服務端可用)

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd 指定接收端套接字描述符;
引數:
	buf 指明一個緩沖區,該緩沖區用來存放recv函式接收到的資料;
	len 指明buf的長度;
	flags 一般置0,
	客戶或者服務器應用程式都用recv函式從TCP連接的另一端接收資料,

close 關閉套接字

1、使用 close 函式即可關閉套接字 關閉一個代表已連接套接字將導致另一端接收到一個 0 長度的資料包
2、做服務器時關閉監聽套接字將導致服務器無法接收新的連接,但不會影響已經建立的連接,關閉 accept 回傳的已連接套接字將導致它所代表的連接被關閉,但不會影響服務器的監聽
3、做客戶端時 關閉連接就是關閉連接,不意味著其他

埠復用

  1. 當出現連接失敗,或者地址被占用等錯誤,可以設定埠復用
  2. 一個程式多個任務系結同一個埠
  3. 一個任務系結多個埠
  4. UDP多播也可以設定埠復用
//設定埠復用
int yes=1;
setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR,&yes, sizeof(yes));
//設定套接字支持廣播
int yes=1;
setsockopt(sockfd, SOL_SOCKET,SO_BROADCAST,&yes, sizeof(yes));
//設定多播
setsockopt(sockfd, IPPROTO_IP,IP_ADD_MEMBERSHIP, &merq, sizeof(merq));

TCP簡單客戶端的撰寫

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <pthread.h>

int main(int argc,char **argv)
{
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server_addr;
    pthread_t pth;

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(6666);
    socklen_t addrlen = sizeof(server_addr);
    inet_pton(AF_INET, "10.36.145.33", &server_addr.sin_addr.s_addr);
    int err = connect(sock_fd, (struct sockaddr *)&server_addr, addrlen);
    // printf("%u\n", INADDR_ANY);
    //    char ip[48];
    // //
    // // send(sock_fd, ip, strlen(ip), 0);
    //   inet_ntop(AF_INET, &server_addr.sin_addr.s_addr, ip, 16);
    //         printf("%s\n", ip);
      if (err != 0)
      {
          perror("socket");
      }

        while (1)
        {
            char buf[63];
            fgets(buf, sizeof(63), stdin);
            send(sock_fd, buf, strlen(buf), 0);
        }
     }
     

TCP簡單服務器的撰寫(只能同時跟一個客戶端交流)

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <pthread.h>

#if 1
/*
功能:只能同時和一個客戶端通信,除非客戶端退出或者發送bye的指令才能連接下一個
*/
#define client_max 10
 struct sockaddr_in cli_addr;
 int sock_fd;
 int sockfd_new;
 int state = 1;
 struct sockaddr_in my_addr;
 socklen_t addrlen;
int main()
{
    // 1.創建套接字
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in my_addr;
    pthread_t pth;

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(6666);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addrlen = sizeof(my_addr);

    //2.服務器系結
   int err=bind(sock_fd, (struct sockaddr *)&my_addr, addrlen);
    if(err == -1)
    {
        perror("socket");
    }

    //3.監聽套接字,由主動變成被動
    err = listen(sock_fd, client_max);
    if(err == -1)
    {
        perror("socket");
    }
    printf("success\n");

    while (1)
    {
    
        char ip[16] = "";
        if(state==1)
        {        
       // bzero(&cli_addr, addrlen);
         //4.等待客戶端連接,否則阻塞
        sockfd_new = accept(sock_fd, (struct sockaddr *)&cli_addr, &addrlen);
                inet_ntop(AF_INET, &cli_addr.sin_addr.s_addr, ip, addrlen);
               printf("%d:%s 已連接\n", sockfd_new, ip); 
       }

            state = 2;          
            char buf[64]="";
            recv(sockfd_new, buf, sizeof(buf), 0);
            printf("%d\n", sockfd_new);
            if(strncmp(buf,"bye",3)== 0||strlen(buf)== 0)
            {
                printf("對方已經關閉\n");
                state = 1;
                break;
            }
    }
    return 0;
}
#endif

TCP三次握手

在這里插入圖片描述
1.源埠號:發送方埠號

2.目的埠號:接收方埠號

3.序列號:本報文段的資料的第一個位元組的序號

4.確認序號:期望收到對方下一個報文段的第一個資料位元組的序號

5.首部長度(資料偏移):TCP報文段的資料起始處距離TCP報文段的起始處有多遠,即首部長度,單 位:32位,即以4位元組為計算單位,

6.保留:占6位,保留為今后使用,目前應置為0

7.緊急URG: 此位置1,表明緊急指標欄位有效,它告訴系統此報文段中有緊急資料,應盡快傳送

8.確認ACK: 僅當ACK=1時確認號欄位才有效,TCP規定,在連接建立后所有傳達的報文段都必須把ACK 置1

9.推送PSH:當兩個應用行程進行互動式的通信時,有時在一端的應用行程希望在鍵入一個命令后立即 就能夠收到對方的回應,在這種情況下,TCP就可以使用推送(push)操作,這時,發送方TCP把PSH 置1,并立即創建一個報文段發送出去,接收方收到PSH=1的報文段,就盡快地(即“推送”向前)交 付給接收應用行程,而不再等到整個快取都填滿后再向上交付

10.復位RST: 用于復位相應的TCP連接

11.同步SYN: 僅在三次握手建立TCP連接時有效,當SYN=1而ACK=0時,表明這是一個連接請求報文段, 對方若同意建立連接,則應在相應的報文段中使用SYN=1和ACK=1.因此,SYN置1就表示這是一個連接請 求或連接接受報文

12.終止FIN:用來釋放一個連接,當FIN=1時,表明此報文段的發送方的資料已經發送完畢,并要求釋 放運輸連接,

13.視窗:指發送本報文段的一方的接收視窗(而不是自己的發送視窗)

14.校驗和:校驗和欄位檢驗的范圍包括首部和資料兩部分,在計算校驗和時需要加上12位元組的偽頭部

15.緊急指標:僅在URG=1時才有意義,它指出本報文段中的緊急資料的位元組數(緊急資料結束后就是 普通資料),即指出了緊急資料的末尾在報文中的位置,注意:即使視窗為零時也可發送緊急資料

16.選項:長度可變,最長可達40位元組,當沒有使用選項時,TCP首部長度是20位元組
在這里插入圖片描述
在這里插入圖片描述

第一次連接:當客戶端呼叫connect函式時,客戶端一個SYN包給服務器給服務器:序列號 =0,確認序號 = 0
在這里插入圖片描述

第二次連接:服務器接收到syn包時,第一次SYN給客戶端:序列號 0 ,確認序號為1
在這里插入圖片描述

第三次連接:客戶端表示收到了并且發一個ack給服務器 :序列號0,確認序號 =1
在這里插入圖片描述

TCP四次揮手

我們回顧一下TCP報頭:
在這里插入圖片描述
四次揮手的條件:呼叫close函式
在這里插入圖片描述
我們由圖可知道:
第一次揮手:序列號= m 確認號 = m+1
在這里插入圖片描述
第二次揮手:序列號=m+1 確認號 = m+1,這個時候回復一個ACK為1
在這里插入圖片描述
第三次揮手:序列號=m+1 確認號 = m+1,再次發一個FIN的報文
在這里插入圖片描述
第四次揮手: 序列號 = m+1 確認號 = m+2 ,最后發送一個ACK應答報文

在這里插入圖片描述
總結:假設是客戶端斷開了連接,先發送一個FIN,服務器回復一個ACK應答,發送完應答后再次發送一個FIN,最后客戶端發送一個ACK確認斷開連接,

問題:為什么斷開連接和ack不能在一個資料包中發送
答:因為只有呼叫close函式才會發送斷開連接資料包,但是ack只要收到對方斷開連接資料包就會回復,ack和fin之間存在時間差,所以不可以同時發送ack和fin,就會產生一端連續發兩次給另一端

問題:主動斷開連接的是客戶端,當收到服務器發送到的ack后,還能在接收資料,

答:因為還沒有揮手完,這個時候客戶端處于半關閉狀態,可以接收資料,

高并發服務器的撰寫(行程版)

概念:以前咱們撰寫的是一個服務器只能同時連接一個客戶端,高并發的服務器是可以同時連接多個客戶端并進行通信,

#if 1
/*
功能:一個服務器同時和多個客戶端通信(使用行程的方法)
*/
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <pthread.h>

#define client_max 10
struct sockaddr_in cli_addr;

int sock_fd;
int sockfd_new;
int state = 1;
int pid;
struct sockaddr_in my_addr;
socklen_t addrlen;

void *pthread (void *arg)
{

}
int main()
{
    // 1.創建套接字
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in my_addr;
    pthread_t pth;

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(6666);//這個埠要和客戶端的埠一樣
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addrlen = sizeof(my_addr);
    //設定埠復用
    int yes = 1;
    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
    //2.服務器系結
    int err = bind(sock_fd, (struct sockaddr *)&my_addr, addrlen);
    if (err == -1)
    {
        perror("socket");
    }

    //3.監聽套接字,由主動變成被動
    err = listen(sock_fd, client_max);
    if (err == -1)
    {
        perror("socket");
    }
    printf("success\n");

    while (1)
    {
        // bzero(&cli_addr, addrlen);
        //4.等待客戶端連接,否則阻塞
        sockfd_new = accept(sock_fd, (struct sockaddr *)&cli_addr, &addrlen);
        pid = fork();
        if (pid < 0)
        {
            printf("fork error\n");
        }
        else if (pid == 0)
        {
            close(sock_fd);//這個套接字用不到
            char ip[16] = "";
            inet_ntop(AF_INET, &cli_addr.sin_addr.s_addr, ip, addrlen);
            printf("已連接%s\n", ip);
            while (1)
            {
                char buf[64] = "";
                recv(sockfd_new, buf, sizeof(buf), 0);
                printf("%d\n", sockfd_new);
                if (strncmp(buf, "bye", 3) == 0 || strlen(buf) == 0)
                {
                    printf("對方已經關閉\n");
                    close(sockfd_new);
                    break;
                }
            }
            exit(1);
        }
        else if (pid > 0)
        {
              close(sockfd_new);//父行程不需要這個,子行程內才需要
        }
    }
    return 0;
}
#endif 

高并發服務器的撰寫(執行緒版)

概念:以前咱們撰寫的是一個服務器只能同時連接一個客戶端,高并發的服務器是可以同時連接多個客戶端并進行通信,


#if 1
/*
功能:一個服務器同時和多個客戶端通信(使用執行緒的方法)
*/
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <pthread.h>

#define client_max 10


int sock_fd;

int pid;

typedef struct 
{
  int sockfd_new;
  char ip[16];
  struct sockaddr_in cli_addr;
} Myadd;

struct sockaddr_in my_addr;
socklen_t addrlen;

void *pthread (void *arg)
{
        Myadd p = *(Myadd *)arg;
       inet_ntop(AF_INET, &p.cli_addr.sin_addr.s_addr, p.ip, 16);
       printf("%d,%s已連接\n",p.sockfd_new, p.ip);
       while (1)
       {

           printf("%p\n", &p);
           char buf[64] = "";
           recv(p.sockfd_new, buf, sizeof(buf), 0);
           printf("%d,%s", p.sockfd_new, buf);

           if (strncmp(buf, "bye", 3) == 0 || strlen(buf) == 0)
           {
               printf("對方已經關閉\n");
               close(p.sockfd_new);
               break;
           }
         }
    
}
int main()
{
    // 1.創建套接字
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in my_addr;
    pthread_t pth;

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(6666);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addrlen = sizeof(my_addr);
    //設定埠復用
    int yes = 1;
    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
    //2.服務器系結
    int err = bind(sock_fd, (struct sockaddr *)&my_addr, addrlen);
    if (err == -1)
    {
        perror("socket");
    }

    //3.監聽套接字,由主動變成被動
    err = listen(sock_fd, client_max);
    if (err == -1)
    {
        perror("socket");
    }
    printf("success\n");

    while (1)
    {
        Myadd add;
 
         bzero(&add.cli_addr, addrlen);
        //4.等待客戶端連接,否則阻塞
        add.sockfd_new = accept(sock_fd, (struct sockaddr *)&add.cli_addr, &addrlen);
        pthread_create(&pth, NULL, pthread, (void*)&add);//創建執行緒
        pthread_detach(pth);	
        usleep(1000);
    }
    return 0;
}
#endif 

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/303285.html

標籤:其他

上一篇:解決Cipher Suites導致的“未能創建 SSL/TLS 安全通道”例外問題

下一篇:(42)java Spring Cloud+Spring boot+mybatis企業快速開發架構之SpringCloud-Gateway過濾器工廠的使用

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more