TCP/IP详解 第十二章(2) linux 网络编程

2023-11-20

转载请声明博主https://mp.csdn.net/console/editor/html/106517098

一、Linux网络概述

①LINUX网络优势

1. 完善的内置网络。和内核结合在一起的网络部分。 I p queue
2.Linux 免费提供了大量支持 Internet 的软件,如 wireshark
3. 用户能通过一些 linux 命令完成内部信息或文件的传输如 scp ftp
scp  openvpncft.tar.gz    root@193.168.20.233:/home/cft/
4. 远程访问 。如 ssh 协议
 

②Linux网络模型 

         

③网络层协议

第一部分为网络层协议。主要包括Internet协议(IP)、网际控制报文(ICMP)和地址解析协议(ARP

.Internet协议(IP

该协议被设计成互联分组交换通信网,以形成一个网际通信环境。它负责在源主机和目的地主机之间传输来自其较高层软件的称为数据报文的数据块,它在源和目的地之间提供非链接型传递服务

*网际控制报文协议(ICMP

它实际上不是IP层部分,但直接同IP层一起工作,报告网络上的某些出错情况。如 ping命令

地址解析协议(ARP

ARP实际上不是网络层部分,它处于IP和数据链路层之间,它是在32IP地址和48位物理地址之间执行翻译的协议 

④传输层协议

第二部分是传输层协议,包括传输控制协议(tcp)和用户数据报文协议(udp

*传输控制协议(TCP):

供可靠的有连接的传输层服务。方式如同打电话,必须先接通才能说话。

*用户数据报文协议(UDP):

UDP提供不可靠的非连接型传输层服务。方式如同发短信,任性的发不用管对方看不看

⑤应用层协议

主要包括Telnet,文件传送协议(FTPTFTP,简单文件传送协议(SMTP,域名服务(DNS,超文本传输协议(http)等协议

⑥协议封装

⑦以太网包

⑧IP协议

IP主要有以下四个主要功能:

            *数据传送

            *寻址

            *路由选择

             *数据报文的分段

IP的主要目的是为数据输入/输出网路提供基本算法,为高层协议提供无连接的传送服务

IP包由IP协议头协议数据两部分构成。

IP协议头结合wireshark分析

IP协议数据报格式

        

     ⑩TCP协议头

       

                TCP协议头(wireshark分析)

          

11、UDP的协议头

二、Linux网络编程基础

①socket

Linux中的网络编程通过Socket(套接字)实现,Socket是一种文件描述符。

Linux中一切设备都是文件。

②Socket有三种类型:

*流式套接字(SOCK_STREAM)    TCP

*数据报套接字(SOCK_DGRAMUDP

*原始套接字(SOCK_RAW)   用于ICMPping

③网络地址

socket程序设计中,struct sockaddr用于记录网络地址:

struct sockaddr

{

  u_short sa_family;

  char sa_data[14];

}

# sa_family:

协议族,采用”AF_XXX”的形式,如:AF_INET(IP协议族)

#sa_data:

14字节的特定协议地址

socket程序设计中,struct sockaddr_in 同样用于记录网络地址

Struct sockaddr_in

{

  short int sin_family;/*协议族*/

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

  struct in_addr sin_addr;  /*协议特定地址*/

  unsigned char sin_zero[8]; /*预留填充0*/

}

编程中一般使用与sockaddr等价的sockaddr_in数据结构

④地址结构

⑤地址转换

IP地址通常由数字加点(192.168.0.1)的形式表示,而在struct in_addr中使用的IP地址是由32位的整数表示,为了转换我们可以使用下面两个函数:

#int inet_aton(const char*cp,struct in_addr  *inp)

#char *inet_ntoa(struct in_addr in)

数里面a代表ascii,n代表network.

Inet_aton是将a.b.c.d形式的IP转换为32位的IP,存储在inp指针里面。Inet_ntoa是将32IP转换为a.b.c.d的格式

⑥字节序转换

不同类型的CPU对变量的字节存储顺序可能不同:有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后,而网络传输的数据顺序是一定要是统一的。所以当内部字节存储顺序和网络字节序(big endian)不同时,就一定要进行转换

为什么要进行字节序转换?

如:

端架构的CPU和小端架构的CPU就会出问题。大端发送一个16位数据0x1234给小端,这是小端就会认为受到了0x3412.

以需要字节序转换,尤其是如果在网络上都遵循大端发送,那么在接收的CPU上进行字节序转换,最方便了。

#htons

   unsigned short类型从主机序转换到网络序

#htonl

   unsigned long类型从主机序转换到网络序

#ntohs

   unsigned short类型从网络序换到主机序

#ntohl

   unsigned long类型从网络序转换到主机序

⑦IP与主机名

在网络中标识一台主机可以用IP地址,也可以使用主机名

struct hostent *gethostbyname(const char *hostname)

 

struct hostent

{

  char *h_name;  /*主机的正式名称*/

  char *h_aliases;/*主机的别名*/

  int  h_addrtype; /*主机的地址类型   AF_INET*/

            int  h_length;   /*主机的地址长度*/

            char **h_addr_list;  /*主机的IP地址列表*/

}

#define h_addr h_addr_list[0] /*主机的第一IP地址*/

⑧socket的编程函数

进行Socket编程的常用函数有:

*socket

建一个socket

*bind

于绑定IP地址和端口号到socket

*connect

该函数用于与服务器建立连接

#listen

置服务器能处理的最大连接请求数

#accept

用来等待来自客户端的socket连接请求

#send

送数据

#recv

接收数据

 

三、TCP网络程序设计

基于TCP-服务器

1.创建一个socket,用函数socket()

2.绑定IP地址、端口等信息到socket上,用函数bind()

3.设置允许的最大连接数,用函数listen()

4.等待来自客户端的连接请求,用函数accept()

5.收发数据,用函数send()recv(),或者read()write()

6.关闭网络连接

②基于TCP-客户端

1.创建一个socket,用函数socket()

2.设置要连接的服务器的IP地址和端口等属性

3.连接服务器,用函数connect()

4.收发数据,用函数sendrecv,或者readwrite

5.关闭网络连接

③基于TCP-通讯模型

④TCP实例分析与演示

tcp_client.c

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

#define BUFSIZE 512
#define MY_PORT 3333
/*堵塞读*/
void read_data_from_common_sockfd(int sockfd)
{
    char text[BUFSIZE] = {'\0'};
    FILE * pf;

    pf = fopen("1.txt","a+");
    while(recv(sockfd,text,BUFSIZE,0)>0){
        printf("client recv:%s\n",text);
        fputs(text,pf);
        memset(text,'\0',BUFSIZE);
    }
    fclose(pf);
}
/*非阻塞读*/
void read_data_from_select_sockfd(int sockfd)
{
    fd_set fdset;
    char read_buf[512] = {'\0'};
    while(1){
        int flag = 0;
        int ret = 0;
        struct timeval timeout = {2,0};
        //timeout.tv_sec = 2;
        //timeout.tv_usec = 0;
        FD_ZERO(&fdset);
        FD_SET(sockfd, &fdset);
        ret = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
       
        if(ret <= 0){
            continue;
        }else{
            if(FD_ISSET(sockfd, &fdset)){
                int tmp_read = 0, all_read = 0;
                while((tmp_read = read(sockfd, read_buf + all_read, sizeof(read_buf))) != 0)
                        all_read += tmp_read;
                        printf("select client recv:%s\n", read_buf);
                        break;
            }
        }
    }
}    

int main(int argc,char *argv[])
{
	int sockfd;
	char buffer[1024];
	struct sockaddr_in server_addr;
	struct hostent *host;

	if(argc != 2){
		fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
		exit(1);
	}	
	
	if((host = gethostbyname(argv[1])) == NULL){
		fprintf(stderr,"Gethostbyname error\n");
		exit(1);
	}

	/*客户程序开始建立 sockfd描述符*/
	if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1){   //AF_INET:IPV4 internet; SOCK_STREAM :TCP
		fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
		exit(1);
	}
	
	/*客户程序填充服务端的资料*/
	bzero(&server_addr,sizeof(server_addr));   //初始化,置0
	server_addr.sin_family = AF_INET;          //IPV4
	server_addr.sin_port = htons(MY_PORT);     //将本机器上的short数据转化为网络上的short数据
	server_addr.sin_addr = *((struct in_addr *) host->h_addr);  //IP地址
	
	/*客户端程序发起连接请求*/
	if(connect(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr)) == -1){
		fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
		exit(1);
	}
	/*连接成功了*/
	printf("Plese input char:\n");
	
	/*发送数据*/
	fgets(buffer,1024,stdin);
	write(sockfd,buffer,strlen(buffer));
	//read_data_from_common_sockfd(sockfd);
	read_data_from_select_sockfd(sockfd);
	/*结束通讯*/
	close(sockfd);
}

tcp_server.c

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

#define MY_PORT 3333

int main(int argc,char *argv[])
{
	int sockfd,new_fd;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	int sin_size;
	int nbytes;
	char buffer[1024];

	/*服务器端开始建立sockfd描述符*/
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){         //AF_INET:IPV4,SOCK_STREAM:TCP
		fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
		exit(1);
	}
	
	/*服务器端填充sockaddr结构*/
	bzero(&server_addr,sizeof(struct sockaddr_in));            //初始化,置0
	server_addr.sin_family = AF_INET;                   //IP
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);      //将本机器上的long数据转化为网络上的long
	//server_addr.sin_addr.s_addr = inet_addr("192.168.1.1"); //用于绑定到一个固定IP,这里使用了inet_addr函数
	server_addr.sin_port = htons(MY_PORT);               //将本机器上的short数据转化为网络上的short格式
	
	/*捆绑sockfd描述符到IP地址*/
	if(bind(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr)) == -1){
		fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
		exit(1);
	}
	
	/*设置允许连接的最大客户数*/
	if(listen(sockfd,5) == -1)
	{
		fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
		exit(1);
	}

	while(1){
		/*服务器阻塞,直到客户程序建立连接*/
		sin_size = sizeof(struct sockaddr_in);
		if((new_fd = accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size)) == -1){
			fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
			exit(1);
		}	
		fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
	
		if((nbytes = read(new_fd,buffer,1024)) == -1){
			fprintf(stderr,"Read Error:%s\n",strerror(errno));
			exit(1);
		}
		
		buffer[nbytes] = '\0';
		printf("Server received %s\n",buffer);
		
		write(new_fd,buffer,strlen(buffer));
	
		/*这个通讯已经结束*/
		close(new_fd);
		/*循环下一个*/
	}
	/*结束通讯*/
}

四、UDP网络程序设计

基于UDP-服务器

1.创建一个socket,用函数socket()

2.绑定IP地址,端口信息到socket上,用函数bind()

3.循环接收数据,用函数recvfrom()

4.关闭网络连接

基于UDP-客户端

1.创建一个socket,用函数socket()

2.绑定IP地址,端口等信息到socket上,用函数bind()

3.设置对方的IP地址和端口等属性

4.发送数据,用函数sendto()

5.关闭网络连接

③基于UDP的通信模型

 

使用tcpdump抓包tcpdump   -i  lo  -s  0  -w  SuccessC2Server.pcap然后用windows端的wireshark打开SuccessC2Server.pcap 文件分析

udp_server.c

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

#define SERVER_PORT 8888
#define MAX_MSG_SIZE 1024

void udps_respon(int sockfd)
{
	struct sockaddr_in addr;
	int addrlen,n;
	char msg[MAX_MSG_SIZE];
	
	while(1){
		/*从网络上读,并写到网络上*/
		bzero(msg,sizeof(msg));   //初始化,置0
		addrlen = sizeof(struct sockaddr);
		n = recvfrom(sockfd,msg,MAX_MSG_SIZE,0,(struct sockaddr*)&addr,&addrlen);   //从客户端接收数据
		msg[n] = '\0';
		/*显示服务端已经收到了消息*/
		fprintf(stdout,"Server have received %s",msg); //显示消息
	} 
}

int main(int argc,char *argv[])
{
	int sockfd;
	struct sockaddr_in addr;
	
	/*建立sockfd描述符*/
        sockfd = socket(AF_INET,SOCK_DGRAM,0);
        if(sockfd < 0){
                fprintf(stderr,"Socket Error:%s\n",strerror(errno));
                exit(1);
        }

	/*服务器端填充sockaddr结构*/
	bzero(&addr,sizeof(struct sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr=htonl(INADDR_ANY);
	addr.sin_port = htons(SERVER_PORT);
	
	/*捆绑sockfd描述符*/
	if(bind(sockfd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) < 0){
		fprintf(stderr,"Bind Error:%s\n",strerror(errno));
		exit(1);
	}
	printf("udp server start ......\n");
	udps_respon(sockfd); //进行读写操作
	close(sockfd);
}

udp_client.c
 

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

#define SERVER_PORT 8888
#define MAX_BUF_SIZE 1024

void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len)
{
    char buffer[MAX_BUF_SIZE];
    int n;
    while(1){
        /*从键盘读入,写到服务器*/
        printf("Please input char:\n");
        fgets(buffer,MAX_BUF_SIZE,stdin);
        sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)addr,len);
        bzero(buffer,MAX_BUF_SIZE);
    }
}

int main(int argc,char *argv[])
{
    int sockfd;
    struct sockaddr_in addr;
    
    if(argc != 2){
        fprintf(stderr,"Usage:%s server_ip\n",argv[0]);
        exit(1);
    }
    
    /*建立sockfd描述符*/
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd < 0){
        fprintf(stderr,"Socket Error:%s\n",strerror(errno));
        exit(1);
    }

    /*填充服务端的资料*/
    bzero(&addr,sizeof(struct sockaddr_in));  //初始化,置0
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    if(inet_aton(argv[1],&addr.sin_addr) < 0){  /*inet_aton函数用于把字符串型的IP地址转化成网络类型IP地址*/
        fprintf(stderr,"Ip Error:%s\n",strerror(errno));
        exit(1);
    }
    
    udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in));   //进行读写操作
    close(sockfd);
}


 

五、Linux并发服务器设计

①服务器模型

在网络程序里面,一般来说都是许多客户对应一个服务器,为了处理客户的请求,对服务器的程序就提出了特殊的请求。目前最常用的服务器模型有:

. 循环服务器: 服务器在同一个时刻只能想要一个客户端的请求

. 并发服务器:服务器在同一时刻可以响应多个客户端的请求

UDP循环服务器

UDP的循环服务器的实现方法:

UDP服务器每次从套接字上读取一个客户端的请求->处理->然后将结构返回给客户机。

  socket(…);

  bind(…);

  while(1){

  recvfrom();

  process();//处理

                    sendto();

  }

TCP循环服务器

TCP循环服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接:
	socket(…);
	bind(…);
	listen(…);
	while(1){
		accept(…);
		process(…);
		close(…);
	}

TCP循环服务器一次只能处理一个客户端的请求。只有在这个客户的所有请求都满足后,服务器才可以继续后面的请求。这样如果有一个客户端占住服务器不放时,其它的客户机都不能工作了,因此,TCP服务器一般很少用循环服务器模型的

  演示:

                     tcp_server.c

                     

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

#define MY_PORT 3333

int main(int argc,char *argv[])
{
	int sockfd,new_fd;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	int sin_size;
	int nbytes;
	char buffer[1024];

	/*服务器端开始建立sockfd描述符*/
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){         //AF_INET:IPV4,SOCK_STREAM:TCP
		fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
		exit(1);
	}
	
	/*服务器端填充sockaddr结构*/
	bzero(&server_addr,sizeof(struct sockaddr_in));            //初始化,置0
	server_addr.sin_family = AF_INET;                   //IP
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);      //将本机器上的long数据转化为网络上的long
	//server_addr.sin_addr.s_addr = inet_addr("192.168.1.1"); //用于绑定到一个固定IP,这里使用了inet_addr函数
	server_addr.sin_port = htons(MY_PORT);               //将本机器上的short数据转化为网络上的short格式
	
	/*捆绑sockfd描述符到IP地址*/
	if(bind(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr)) == -1){
		fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
		exit(1);
	}
	
	/*设置允许连接的最大客户数*/
	if(listen(sockfd,5) == -1)
	{
		fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
		exit(1);
	}

	while(1){
		/*服务器阻塞,直到客户程序建立连接*/
		sin_size = sizeof(struct sockaddr_in);
		if((new_fd = accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size)) == -1){
			fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
			exit(1);
		}	
		fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
	
		if((nbytes = read(new_fd,buffer,1024)) == -1){
			fprintf(stderr,"Read Error:%s\n",strerror(errno));
			exit(1);
		}
		
		buffer[nbytes] = '\0';
		printf("Server received %s\n",buffer);
		
		write(new_fd,buffer,strlen(buffer));
	
		/*这个通讯已经结束*/
		close(new_fd);
		/*循环下一个*/
	}
	/*结束通讯*/
}

                     tcp_client.c 多客户端

   

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

#define BUFSIZE 512
#define MY_PORT 3333
/*堵塞读*/
void read_data_from_common_sockfd(int sockfd)
{
    char text[BUFSIZE] = {'\0'};
    FILE * pf;

    pf = fopen("1.txt","a+");
    while(recv(sockfd,text,BUFSIZE,0)>0){
        printf("client recv:%s\n",text);
        fputs(text,pf);
        memset(text,'\0',BUFSIZE);
    }
    fclose(pf);
}
/*非阻塞读*/
void read_data_from_select_sockfd(int sockfd)
{
    fd_set fdset;
    char read_buf[512] = {'\0'};
    while(1){
        int flag = 0;
        int ret = 0;
        struct timeval timeout = {2,0};
        //timeout.tv_sec = 2;
        //timeout.tv_usec = 0;
        FD_ZERO(&fdset);
        FD_SET(sockfd, &fdset);
        ret = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
       
        if(ret <= 0){
            continue;
        }else{
            if(FD_ISSET(sockfd, &fdset)){
                int tmp_read = 0, all_read = 0;
                while((tmp_read = read(sockfd, read_buf + all_read, sizeof(read_buf))) != 0)
                        all_read += tmp_read;
                        printf("select client recv:%s\n", read_buf);
                        break;
            }
        }
    }
}    

int main(int argc,char *argv[])
{
	int sockfd;
	char buffer[1024];
	struct sockaddr_in server_addr;
	struct hostent *host;

	if(argc != 2){
		fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
		exit(1);
	}	
	
	if((host = gethostbyname(argv[1])) == NULL){
		fprintf(stderr,"Gethostbyname error\n");
		exit(1);
	}

	/*客户程序开始建立 sockfd描述符*/
	if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1){   //AF_INET:IPV4 internet; SOCK_STREAM :TCP
		fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
		exit(1);
	}
	
	/*客户程序填充服务端的资料*/
	bzero(&server_addr,sizeof(server_addr));   //初始化,置0
	server_addr.sin_family = AF_INET;          //IPV4
	server_addr.sin_port = htons(MY_PORT);     //将本机器上的short数据转化为网络上的short数据
	server_addr.sin_addr = *((struct in_addr *) host->h_addr);  //IP地址
	
	/*客户端程序发起连接请求*/
	if(connect(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr)) == -1){
		fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
		exit(1);
	}
	/*连接成功了*/
	printf("Plese input char:\n");
	
	/*发送数据*/
	fgets(buffer,1024,stdin);
	write(sockfd,buffer,strlen(buffer));
	//read_data_from_common_sockfd(sockfd);
	read_data_from_select_sockfd(sockfd);
	/*结束通讯*/
	close(sockfd);
}

④TCP并发服务器       

并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是有服务器创建一个子进程来处理:
	socket(…);
	bind(…);
	listen(…);
	while(1){
		accept(…);
		if(fork(…) == 0){
			process(…);
			close(…);
			exit(…);
		}
		close(…);
	}

⑤TCP并发服务器实例分析与演示     

  

Tcp_server_fork.c  结合网络分析工具tcp_dump,重点是讲怎么用tcpdump抓包文件格式为.pcap以后,怎么用wireshark分析这个文件

Tcp_server_fork.c 

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

#define MY_PORT 3333

int main(int argc,char *argv)
{
	int listen_fd,accept_fd;
	struct sockaddr_in client_addr;
	int n;
	int nbytes;
	if((listen_fd = socket(AF_INET,SOCK_STREAM,0)) < 0){   //AF_INET:IPV4; SOCK_STREAM:TCP;
		printf("Socket Error: %s\n\a",strerror(errno));
		exit(1);
	}
	
	bzero(&client_addr,sizeof(struct sockaddr_in));    //初始化 置0
	client_addr.sin_family = AF_INET;                   //AF_INET : IPV4
	client_addr.sin_port = htons(MY_PORT);             //将本机端口号转化成short
	client_addr.sin_addr.s_addr = htonl(INADDR_ANY);   //绑定任何一个IP地址
	//server_addr.sin_addr.s_addr = inet_addr("192.168.1.1"); //用于绑定到一个固定IP,这里使用了inet_addr函数
	n = 1;
	
	/*如果服务器终止后,服务器可以第二次快速启动而不用等待一段时间*/
	setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int));       

	if(bind(listen_fd,(struct sockaddr*)&client_addr,sizeof(client_addr)) < 0){
		printf("Bind Error:%s\n\a",strerror(errno));
		exit(1);
	}

	listen(listen_fd,5);
	
	while(1){
		
		accept_fd = accept(listen_fd,NULL,NULL);
		/*EINTR means that a syscall is interrupted by a signal. You should restart it if you want it to work properly as the signal doesnt exist.*/
		/* 慢系统调用(slow system call):此术语适用于那些可能永远阻塞的系统调用。永远阻塞的系统调用是指调用有可能永远无法返回,多数网络支持函数都属于这一类。如:若没有客户连接到服务器上,那么服务器的accept调用就没有返回的保证。
		EINTR错误的产生:当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。例如:在socket服务器端,设置了信号捕获机制,有子进程,当在父进程阻塞于慢系统调用时由父进程捕获到了一个有效信号时,内核会致使accept返回一个EINTR错误(被中断的系统调用)。*/
		if((accept_fd < 0) && (errno == EINTR)){
			continue;
		}else if(accept_fd < 0){
			printf("Accept Error:%s\n\a",strerror(errno));
			continue;
		}

		if((n = fork()) == 0){
			/*子进程处理客户端连接*/
			char buffer[1024];
			
			if((nbytes = read(accept_fd,buffer,1024)) == -1){
				fprintf(stderr,"Read Error:%s\n",strerror(errno));
				exit(1);
			}
			buffer[nbytes] = '\0';
			
			printf("Server received %s\n",buffer);
			close(listen_fd);
			close(accept_fd);
			exit(0);
		}else{
			close(accept_fd);
		}
		
	}
	
}

tcp_client.c 多客户端

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

#define BUFSIZE 512
#define MY_PORT 3333
/*堵塞读*/
void read_data_from_common_sockfd(int sockfd)
{
    char text[BUFSIZE] = {'\0'};
    FILE * pf;

    pf = fopen("1.txt","a+");
    while(recv(sockfd,text,BUFSIZE,0)>0){
        printf("client recv:%s\n",text);
        fputs(text,pf);
        memset(text,'\0',BUFSIZE);
    }
    fclose(pf);
}
/*非阻塞读*/
void read_data_from_select_sockfd(int sockfd)
{
    fd_set fdset;
    char read_buf[512] = {'\0'};
    while(1){
        int flag = 0;
        int ret = 0;
        struct timeval timeout = {2,0};
        //timeout.tv_sec = 2;
        //timeout.tv_usec = 0;
        FD_ZERO(&fdset);
        FD_SET(sockfd, &fdset);
        ret = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
       
        if(ret <= 0){
            continue;
        }else{
            if(FD_ISSET(sockfd, &fdset)){
                int tmp_read = 0, all_read = 0;
                while((tmp_read = read(sockfd, read_buf + all_read, sizeof(read_buf))) != 0)
                        all_read += tmp_read;
                        printf("select client recv:%s\n", read_buf);
                        break;
            }
        }
    }
}    

int main(int argc,char *argv[])
{
	int sockfd;
	char buffer[1024];
	struct sockaddr_in server_addr;
	struct hostent *host;

	if(argc != 2){
		fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
		exit(1);
	}	
	
	if((host = gethostbyname(argv[1])) == NULL){
		fprintf(stderr,"Gethostbyname error\n");
		exit(1);
	}

	/*客户程序开始建立 sockfd描述符*/
	if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1){   //AF_INET:IPV4 internet; SOCK_STREAM :TCP
		fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
		exit(1);
	}
	
	/*客户程序填充服务端的资料*/
	bzero(&server_addr,sizeof(server_addr));   //初始化,置0
	server_addr.sin_family = AF_INET;          //IPV4
	server_addr.sin_port = htons(MY_PORT);     //将本机器上的short数据转化为网络上的short数据
	server_addr.sin_addr = *((struct in_addr *) host->h_addr);  //IP地址
	
	/*客户端程序发起连接请求*/
	if(connect(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr)) == -1){
		fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
		exit(1);
	}
	/*连接成功了*/
	printf("Plese input char:\n");
	
	/*发送数据*/
	fgets(buffer,1024,stdin);
	write(sockfd,buffer,strlen(buffer));
	//read_data_from_common_sockfd(sockfd);
	read_data_from_select_sockfd(sockfd);
	/*结束通讯*/
	close(sockfd);
}

六、HTTP程序设计

①HTTP简介

. HTTP(Hypertext Transfer Protocol)超文本传输协议,它是web的核心,在[RFCF1945][RFC 2616]中进行定义。

 

. HTTP由两个程序实现:一个客户端程序和一个服务器程序。客户程序和服务器程序运行在不同的端系统中,通过交换HTTP报文进行会话

 

.HTTP是一个基于请求/响应模式的、无状态的协议。

 

.目前常用版本是HTTP1.0 HTTP1.1     http2和http3大部分互联网平台已经支持

②HTTP持续与非持续连接

嵌入到网页中的超文本概念可能需要多个请求和应答。如果网页,这个被获取的对象,位于不同的服务器,那么我们没有其他选择只能每获取一个对象就要创建一个新的TCP连接。然而,如果某些对象是位于同一台服务器的,我们可以有两种选择:一是每次使用一个新的TCP连接获取一个对象。二是创建一个TCP连接获取全部对象。第一种方法称为非持续连接,第二种方法称为持续连接

③HTTP与URL

 总结: URL 它定义了相关网页的地址和名称。

每个URL地址有两部分组成:存放对象的服务器主机名和对象的路径。

例如:

URL地址http://www.someSchool.edu/someDepartment/picture.gif,其中 http协议www.someSchool就是主机名,没写端口号为默认的80/someDepartment/picture.gif就是路径

这也是遵守Restful规则

④HTTP URL Restful接口规则

就是在你的url地址里面要能看到分类,比如查询用户信息类的都用/userxxx/xxx,查询公司信息类的就用/companyxx/xxx类似这样

例如:

http://ip:端口/v2/userInfo?userName=cao

http://ip:端口/v2/companyInfo?departName=开发部

HTTP报文格式和响应报文

HTTP请求请求行

请求行一个方法符号开头,后面跟着请求URL和协议的版本,以CRLF(\r\n)作为结尾。请求行以空格分隔,除了作为结尾的CRLF外,不允许出现单独的CR或者LF字符。格式如下

  Method   Request-URL HTTP-Version CRLF

Method表示请求的方法,Request-URI是一个统一资源标识符,标识了要请求的资源,HTTP-Version表示请求的HTTP协议版本,CRLF表示回车换行。例如:

  GET  /form.html  HTTP/1.1 (CRLF)

⑦HTTP请求-方法GET

.GET方法用于获取由Request-URL所标识的资源的信息,常见的形式是:

  GET  Request-URL HTTP/1.1 (CRLF)

.当我们通过在浏览器的地址栏中直接输入网址的方式去访问网页的时候,浏览器采用的就是GET方法向服务器获取资源

 

HTTP 方法:GET 对比 POST  网址:

http://www.w3school.com.cn/tags/html_ref_httpmethods.asp

⑧HTTP请求方法POST

.POST方法用于向目的服务器发出请求,要求服务器接受附在请求后面的数据。POST方法在表单提交的时候用得较多。

.采用POST方法提交表单的例子

  POST    /reg.jsp   HTTP/1.1   (CRLF)

  Accept: image/gif   …..(因为篇幅关系省略)  (CRLF)

  ……因为篇幅关系省略

  Host: www.winsunlight.com  (CRLF)

  Content-Length: 22  (CRLF)

              Connection: Keep-Alive  (CRLF)

  Cache-Control: no-cache (CRLF)

  (CRLF)

              user=zhangsan&pwd=1234

⑨HTTP请求 方法HEAD

HEAD方法与GET方法几乎是一样的,它们的区别在于HEAD方法只是请求消息报头,而不是完整的内容。对于HEAD请求的回应部分来说,它的HTTP头部中包含的信息与通过GET请求所得的信息是相同的。利用这个方法,不必传输整个资源的内容,就可以得到Request-URL所标识的资源的信息。这个方法通常被用于测试超链接的有效性,是否可以访问,以及最近是否更新。

HTTP响应状态行

. 状态行由协议版本、数字形式的状态代码、及相应的状态描述组成,各元素之间以空格分隔,除了结尾的CRLF(回车换行)序列外,不允许出现CRLF字符。格式如下:

  HTTP-Version Status-Code Reason-Phrase CRLF

HTTP-Version表示服务器HTTP协议的版本,Status-Code表示服务器发回的响应代码,Reason-Phrase表示状态代码的文本描述,CRLF表示回车换行。例如:

  HTTP/1.1   200  OK  (CRLF)

11、HTTP响应状态代码与状态描述(1

.状态代码由3位数字组成,表示请求是否被理解或者被满足,状态描述给出了关于状态代码的简短的文本描述。

.状态代码的第一个数字定义了响应的类别,后面两位数字没有具体的分类。第一个数字有五种可能的取值:

-- 1xx: 指示信息 ---表示请求已接收,继续处理

-- 2xx: 成功---表示请求已经被成功接收、理解、接受。

-- 3xx:重定向---表示要完成请求必须进行更进一步的操作。

-- 4xx:客户端错误--- 表示请求有语法错误或请求无法实现

-- 5xx:服务器端错误---服务器未能实现合法的请求。

12、HTTP响应状态代码与状态描述(2

一些常见的状态码和相关的短语包括:

. 200  OK : 请求成功,信息在返回的响应报文中。

.301   Moved Permanetly : 请求的对象已经被永久转移,新的URL定义在响应报文的Location:首部行中。客户软件将自动获取新的URL

.400  Bad Requset: 一个通用差错代码,指示该请求不能被服务器理解。

.404 Not Found: 被请求的文档不在服务器上。

.505 HTTP Version Not Supported: 服务器不支持请求报文使用的HTTP协议版本。

13、HTTP实例分析

tcp_client_http.c

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

#define BUFSIZE 2048

int creat_socket(char *server_ip,int server_port);
void send_data_to_sockfd(int sockfd,char send_buf[],int send_buf_len);
int read_data_from_common_sockfd(int sockfd);
void make_http_requset_header(int sockfd,char *url,char *server_ip,char *server_port);

/*创建套接字并且连接到服务器*/
int creat_socket(char *server_ip,int server_port)
{
	int sockfd = 0;
	struct sockaddr_in server_addr;
	struct hostent *host;
	
	if((host = gethostbyname(server_ip)) == NULL){
		fprintf(stderr,"Gethostbyname error\n");
		exit(1);
	}

	/*客户程序开始建立 sockfd描述符*/
	if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1){   //AF_INET:IPV4 internet; SOCK_STREAM :TCP
		fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
		exit(1);
	}
	
	/*客户程序填充服务端的资料*/
	bzero(&server_addr,sizeof(server_addr));   //初始化,置0
	server_addr.sin_family = AF_INET;          //IPV4
	server_addr.sin_port = htons(server_port);     //将本机器上的short数据转化为网络上的short数据
	server_addr.sin_addr = *((struct in_addr *) host->h_addr);  //IP地址
	
	/*客户端程序发起连接请求*/
	if(connect(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr)) == -1){
		fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
		exit(1);
	}
	return sockfd;
}

/*向socket中发送数据*/
void send_data_to_sockfd(int sockfd,char send_buf[],int send_buf_len)
{
 	int tmp_send = 0, all_send = 0;
    while((tmp_send = send(sockfd, send_buf + all_send, strlen(send_buf) - all_send,0)) > 0)
		all_send += tmp_send;
}

/*从socket中非阻塞方式读数据*/
int read_data_from_common_sockfd(int sockfd)
{
	char readbuf[BUFSIZE] = {'\0'};
	int  fd;
	
	fd = open("./1.txt", O_WRONLY|O_CREAT);
	//snprintf(readbuf, sizeof(readbuf) - 1, "%s", "echo " " > 1.txt");  //清空1.txt
	//system(readbuf);
	//memset(readbuf,'\0',BUFSIZE);  
	while(recv(sockfd,readbuf,BUFSIZE,0)>0){
    	write(fd, readbuf, BUFSIZE);
    }
	close(fd);
    printf("client recv:%s\n",readbuf);
	
    char *p = strstr(readbuf, "\r\n\r\n");
    if(!p)
    {
        printf("read_buf no '\\r\\n\\r\\n' \n");
		return -1;
	}
	memset(readbuf,'\0',BUFSIZE);  
	return 0;
}

/*打包http的头文件并发送*/
void make_http_requset_header(int sockfd,char *url,char *server_ip,char *server_port)
{
	char header_buf[BUFSIZE] = {'\0'};
	//strnset(header_buf,'/0',BUFSIZ);
	printf("make http requset header ...\n");
	strcat(header_buf,"GET / ");
	if(url){
		strcat(header_buf,url);
	}
    strcat(header_buf,"HTTP/1.1\r\n");
    strcat(header_buf,"Accept: */*\r\n");
    strcat(header_buf,"Accept-Language: zh-cn\r\n");
    strcat(header_buf,"User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)\r\n");
    strcat(header_buf,"Accept-Encoding: gzip, deflate\r\n");  
    strcat(header_buf,"Host:");
    strcat(header_buf,server_ip);
	strcat(header_buf,":");
	strcat(header_buf,server_port);
    //strcat(header_buf,"\r\nConnection: Keep-Alive\r\n");  //服务器会一直阻塞不发送close,客户机可以继续发送数据
    strcat(header_buf,"\r\nConnection: Close\r\n");       //服务器处理完一次数据就会close,客户机必须重新建立连接才能发送数据
    strcat(header_buf,"\r\n");

	printf("header_buf: %s\n",header_buf);
	send_data_to_sockfd(sockfd,header_buf,strlen(header_buf));
}


int main(int argc,char *argv[])
{
	int sockfd;
	int port;
	if(argc != 3){
        fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
        exit(1);
    }
	port = atoi(argv[2]);
	/*连接成功了*/
	sockfd = creat_socket(argv[1],port);
	printf("creat socket sucessful!!!:\n");
	
	/*发送数据*/
	make_http_requset_header(sockfd,NULL,argv[1],argv[2]);
	
	/*接收数据*/
	read_data_from_common_sockfd(sockfd);
	/*关闭网络连接*/
   	close(sockfd);
}


访问公司服务器 192.168.32.62 端口号 8280

例如:打开两个终端

第一个终端 :

Tcpdump  -ni eth1 -s  0  –w  xxxx.pcap 

 

第二个终端:

./tcp_client_http   192.168.32.62    8280

 

wireshark分析xxxx.pcap分析

14、HTTP数据通信JSON

简单来说:用大括号表示的字符串结构体

 

  分析两个程序:

  1.cjson_test1

  2.cjson_test2

下载链接:

           https://download.csdn.net/download/caofengtao1314/12491681

七、参考资料

Wireshark使用网站:http://www.cr173.com/html/20128_all.html

Tcpdump使用网站:

http://blog.csdn.net/langeldep/article/details/6156818

http://gaodi2002.blog.163.com/blog/static/232076820061023113452176/

参考书:TCP/IP详解第一卷 

                  UNIX网络编程第一卷

                  UNIX环境高级编程

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TCP/IP详解 第十二章(2) linux 网络编程 的相关文章

  • R----dplyr包介绍学习

    dplyr包 plyr包的替代者 专门面对数据框 将ddplyr转变为更易用的接口 gt 来自dplyr包的管道函数 其作用是将前一步的结果直接传参给下一步的函数 从而省略了中间的赋值步骤 可以大量减少内存中的对象 节省内存 可惜的是应用范
  • 【理解springboot自动装配原理】

    理解springboot自动装配原理 最近读了小马哥 mercyblitz Springboot编程思想 核心篇 有了一些心得和感悟 分享给大家 1 官网介绍了激活自动装配的方法 文档提到激活自动化装配的注解 EnableAutoConfi
  • DAS、SAN、NAS存储连接方式详解

    1 直接访问存储DAS Direct Access Storage DAS将存储设备通过SCSI接口或光纤通道直接连接到一台计算机上 代表为磁盘阵列柜RAID 磁盘阵列柜是由多个硬盘按照不同的方式组合成一个大型的磁盘组 利用个别磁盘提供数据
  • Spring的xml文档配置

    1基于XML的注解配置
  • webpack 收集依赖、打包输出精简实现

    文章目录 安装babel插件 读取文件信息 获取当前js文件的依赖关系 广度遍历获取所有依赖图 生成浏览器可执行代码 安装babel插件 由于ES6转ES5中需要用到babel 所以要用到一下插件 npm install babel cor
  • MATLAB-DL6

    MATLAB DL6 步骤 交互式迁移貌似2020a才有 学会用analyze network 命令行式 迁移学习 冻结 freezeWeights createLgraphUsingConnections 数据增强 学习参数 函数大杂烩
  • SQL求解用户连续登录天数

    数据分析面试过程中 一般都逃不掉对SQL的考察 可能是笔试的形式 也可能是面试过程中面试官当场提问 当场在纸上写出 或者简单说一下逻辑 今天 就来分享一道面试中常常被问到的一类SQL问题 连续问题 无论是什么样的场景 只要是 连续 问题 那
  • TCP/IP协议之服务器端——华清远见

    咳咳咳 今天也是认真学习的一天 一 TCP IP协议是什么 TCP协议是一种以固连线为基础的协议 它提供两台计算机之间可靠的数据传送 TCP可以保证从一端数据传至连接的另一端时 数据能够确实送达 TCP协议适合可靠性比较高的场合 就像拨打电
  • 队列的几种实现方式

    队列简介 队列是一种特殊的线性表 特殊之处在于它只允许在表的前端 front 进行删除操作 而在表的后端 rear 进行插入操作 和栈一样 队列是一种操作受限制的线性表 进行插入操作的端称为队尾 进行删除操作的端称为队头 队列是一种最常用的
  • Android10(Q)系统源码编译

    Android10系统编译 一 硬件环境 二 软件环境 三 开始编译 四 遇到问题 一 硬件环境 在ubuntu18 04系统中下载编译android10 Q 源码需要如下条件 1 至少4G内存 小于4G内存编译源码期间的等待将会是很痛苦的
  • 【数学建模】数据处理问题

    一 插值与拟合 常用于数据的补全以及趋势分析 1 插值 总的思想 就是利用函数f x 若干已知点的函数值 求出适当的特定函数g x 这样f x 其他未知点上的值 就可以用g x 在这一点的值来近似 这种通过已知求未知的方法称为 插值 插值方
  • mysql知识系列:查看用户密码、修改用户密码,对网上“update user set authentication_string=‘123456’ where user=‘root’;”纠错

    说明 博主用的是mysql8 0 18 网上在找回mysql密码 清一色的教程都是修改root用户的密码 并且使用 update user set authentication string 123456 where user root 博
  • Keycloak概述

    这里写自定义目录标题 Keycloak概述 Single Sign On Kerberos 社交登录 用户合并 客户端适配 管理控制台 用户管理控制台 标准协议 授权服务 Getting Started Keycloak概述 keycloa
  • FPN网络详解

    1 特征金字塔 特征金字塔 Feature Pyramid Networks FPN 的基本思想是通过构造一系列不同尺度的图像或特征图进行模型训练和测试 目的是提升检测算法对于不同尺寸检测目标的鲁棒性 但如果直接根据原始的定义进行FPN计算
  • mysql报错ERROR 1356 (HY000): View ‘mysql.user‘ references invalid table(s) or column(s) or function(s)

    当您在使用 UPDATE user SET password PASSWORD newpassword WHERE User root 命令时提示 ERROR 1356 HY000 View mysql user references in
  • c语言数组下标和指针,C语言 数组 下标与指针 效率解析

    以字符串拷贝函数为例 解析数组中下标与指针的效率情况 指针的效率至少和下标相同 原因参考C下标的实现原理 注意编译器差异 因为部分编译器针对下标设置了特殊汇编指令 不做考虑 define SIZE 50 int x SIZE int y S
  • SQL中join group by having max() 时转Linq

    本来开发时有一个分组聚合的脚本 比较复杂 为了笔记效果 所以将脚本做一个简化 本来库里有两个表TableA和TableB 两个表的主键做如下关联 TableA的主键ID为TableB的外键Aid SELECT a Id a Name b I
  • 【Android11系统开发】上层app通过AIDL监听framework数据

    一 适用场景 在Android系统开发中 需要监听按键 触摸 或者可见窗口大小变化等需求时 你会考虑什么方法来实现呢 通过广播的方式可以实现 但是效果可能并不好 AIDL可以实现跨进程通讯 可以解决以上需求 下面重点分析下如何具体实现 以实
  • Node.js事件循环

    在 Node js 中 事件循环是用来处理非阻塞 I O 的基础 这意味着在 Node js 中 用户代码不会因为等待 I O 操作而停止执行 而是在 I O 操作完成后被通知 Node js 中的事件循环的工作方式有以下几种 首先 Nod

随机推荐

  • 【elementplus】body设置zoom后,el-table开启show-overflow-tooltip后,表格的tooltip显示会错位的解决方案

    由于我的项目是无法避免使用zoom 所以只记录zoom后的解决方案 示例 明明划过的是第一行 tooltip却显示到了第四行的位置 正确显示 划过第一行 tooltip显示在第一行的位置 代码 使用transform属性来修复el tabl
  • JavaScript 实现html导出为PDF文件

    相信各位前端工程狮们在一些报表项目 管理系统项目中都会遇到在这样的需求 申请报 表格 简历等等图文信息有导出为PDF文件 下面是记录我在项目中完成该需求的代码dome 发布出来也是希望对大家有些帮助 1 整体思路 将HTML元素打印或导出为
  • 【满分】【华为OD机试真题2023 JS】统计匹配的二元组个数

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 统计匹配的二元组个数 知识点数组 时间限制 1s 空间限制 32MB 限定语言 不限 题目描述 给定两个数组A和B 若数组A的某个元素A i 与数组B中的某个元素B j 满足 A
  • 函数getopt(),及其参数optind

    getopt被用来解析命令行选项参数 转载地址 http hi baidu com xlt1888 blog item 703148383008492670cf6c2d html include
  • java属于什么语言_java是什么语言 ?是什么系统?

    一开始了解计算机这个专业 大家都会经常性听到Java这一词语 那么大家有真正的了解什么是Java吗 Java是属于什么语言呢 JAVA语言 其实是混合型的一种语言 Java语言是一个支持网络计算的面向对象程序设计语言 Java语言吸收了Sm
  • MinIO学习文档(Java版)

    目录 一 安装 1 在k8s中安装minio单机版 1 创建minio名称空间 2 minio单机版安装yaml 二 代码 1 pom xml 说明 minio所用依赖 2 application yml 说明 放置minio连接信息 mi
  • Python爬虫入门——梦开始的地方

    目录 文章目录 前言 一 前置知识 二 实现步骤 1 分析网站 2 制定爬取方案 3 实现方案 4 基础代码展示 5 代码详解 6 代码封装 总结 前言 爬虫应严格遵守国家法律规定 时隔数月 进入暑假 回忆这数月的学习内容 不禁感慨计算机的
  • jpa insert 对象_SpringBoot2.x入门:使用JPA

    前提 这篇文章是 SpringBoot2 x入门 专辑的 第9篇 文章 使用的SpringBoot版本为2 3 1 RELEASE JDK版本为1 8 这篇文章会介绍一下SpringBoot如何引入和使用JPA JPA 即Java Pers
  • 表示不变量

    不变量 产生好的ADT设计 其中最重要的一点就是它会保护 保留自己的不变量 不变量是一种属性 它在程序运行的时候总是一种状态 而不变性就是其中的一种 一旦一个不变类型的对象被创建 它总是代表一个不变的值 当一个ADT能够确保它内部的不变量恒
  • Java内存模型

    Java内存模型 处理器需要与内存交互 如读取运算数据 存储运算结果等 这个I O操作是很难消除的 无法仅靠寄存器来完成所有运算任务 由于计算机的存储设备与处理器的运算速度有几个数量级的差距 所以现代计算机系统都不得不加入一层读写速度尽可能
  • GitHub上传新手

    第一次上传 1 注册GitHub 2 新建仓库 网上有新建教程 很简单的 3 下载安装Git 一般一直下一步即可 没有特殊要求的话 网址 https gitforwindows org 4 在Git Bush中进入放项目文件的地址 如果是直
  • CentOS7上安装anaconda3及其遇到的问题

    https mirrors tuna tsinghua edu cn anaconda archive 国内镜像 下载速度快 不要选择最新版 有问题 建议 4 3 0 运行命令 conda V 返回版本号即安装成功 1 通过命令行工具 wg
  • 必须了解的mysql三大日志-binlog、redo log和undo log

    目录 一 前言 二 binlog 备份日志 1 作用 2 使用场景 3 日志形式 4 binlog刷盘时机 三 redo log 重做日志 1 概念 2 为什么需要redo log 3 日志形式 4 redo log与binlog区别 四
  • 锂电池基础知识简介

    锂离子电池已经渗透到现代生活的方方面面 它可以为生活中很多东西 如手机 手表 平板电脑 便携式设备等 提供电源 现简单概要介绍锂电池的基础知识 1 基础术语 1 C rate 倍率 指电池充放电时电流与电池标称容量的比率 即描述了电池可以在
  • FPGA Lattice Diamond 开发环境搭建

    FPGA Lattice Diamond 开发环境搭建 Lattice Diamond 软件下载 在浏览器中输入 Lattice 的官网地址 http www latticesemi com 进入官网首页在上方选择产品系列选项 出现如下图所
  • Unity学习笔记(一)—— 基础知识

    一 基础知识 1 开发团队组成 2 unity特点 图形界面 所见即所得 入门简单 支持C 比OC C 更友好 js 国内外资源丰富 因为使用的人多 跨平台性好 PC端 移动端等 对VR AR的支持最完善 3 成功案例 游戏 炉石传说 神庙
  • 【源码篇】基于ssm+bootstrap+jquery的学生成绩管理系统

    系统介绍 基于ssm bootstrap jquery的学生成绩管理系统一共分为六大模块 分别是用户管理 课程管理 班级管理 学籍管理 学费管理 成绩管理 用户管理 1 用户信息预览 查询并根据姓名搜索系统用户 2 新增用户信息 添加系统用
  • Redis7之事务(五)

    五 Redis 事务 5 1 介绍 可以一次执行多个命令 本质是一组命令的集合 一个事务中的所有命令都会序列化 按顺序地串行化执行而不会被其他命令插入 不许加塞 一个队列中 一次性 顺序性 排他性的执行一系列命令 5 2 Redis事务和数
  • 包管理工具那么多,怎么选?npm、yarn 和 pnpm 三者比较及使用详解!

    1 简介 在 Vue 项目中 我们需要使用许多第三方依赖库 如 Vue Router Vuex Axios Element UI 等等 这些依赖库通常以 NPM 包的形式提供 而且在使用时需要进行版本管理 因此 我们需要使用一个包管理工具来
  • TCP/IP详解 第十二章(2) linux 网络编程

    转载请声明博主https mp csdn net console editor html 106517098 一 Linux网络概述 LINUX网络优势 1 完善的内置网络 和内核结合在一起的网络部分 I p queue 2 Linux 免