Linux应用——socket函数及TCP通信

news2024/12/29 9:52:46

        网络通信实质上也是实现进程间通信,只是与之前进程间通信不同的是,现在在不同的计算机上进行进程间通信。比如:利用QQ工具实现聊天,在两个电脑上有不同的QQ进程之间在通信。而网络通信是如何使用进程间通信呢?采用的是socket技术。下面将对socket技术进行学习;

一、socket介绍

           所谓 socket(套接字),本身的意思是插座的意思。对网络中不同主机上的应用进程之间进行双向通信的端点的抽象 。socket将数据包的层层协议进行简化了,将四层网络模型简化成端到端的通信。一个sicket就是网络上进程通信的一端。它能够将层层协议的数据封装好,我们只需要学习使用对应的API即可。

        socket 可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的 API,也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 socket 中,该 socket 通过与网络接口卡(NIC)相连的传输介质将这段信息送到另外一台主机的 socket 中,使对方能够接收到这段信息。socket 是由 IP 地址端口结合的,提供向应用层进程传送数据包的机制。

        LINUX中一切皆文件!socket质为内核借助缓冲区形成的伪文件。既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux 系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递

socket通信分两部分:
- 服务器端:被动接受连接,一般不会主动发起连接
- 客户端:主动向服务器发起连接
socket是一套通信的接口,Linux 和 Windows 都有,但是有一些细微的差别.

        socket是通过文件描述符操作数据,先通过fd往主机A中的写缓冲区写入数据,数据经过四层网络模型加上对应的封装,再通过数据链路层传输至主机B,主机B经过分用,将数据传至读缓冲区,主机B的fd通过读缓冲区获得主机A传递的数据。我们只需要调用API进行数据的读与写,不需要去查看底层是如何实现的。

二、字节序

2.1简介

        现代 CPU 的累加器一次都能装载(至少)4 字节(这里考虑 32 位机),即一个整数。那么这 4字节在内存中排列的顺序将影响它被累加器装载成的整数的值,这就是字节序问题。

        字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。

        字节序分为大端字节序(Big-Endian)小端字节序(Little-Endian)。大端字节序是指一个整数的最高位字节(23 ~ 31 bit)存储在内存的低地址处,低位字节(0 ~ 7 bit)存储在内存的高地址处;小端字节序则是指整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。

2.2举例

小端字节序:

0X 01 02 03 04 四个字节;

内存方向:低位——高位;

内存存储顺序:04 03 02 01(高字节在高位,低字节在低位);

大端字节序:

0X 01 02 03 04 四个字节 ;

内存方向:低位——高位;

内存存储顺序: 01 02 03 04(高字节在低位,低字节在高位);

不同计算机的存储方向不同,那如果不同大小端的主机进行通信时, 如果不达成一致的规则,通信双方将无法进行正确的编码/译码从而导致通信失败。

2.3判断大小端

#include <stdio.h>

int main()
{
union 
{
    short value;//两个字节;
    char bytes[sizeof(short)];//char[2];
}text;
    
text.value=0x0102;
if(text.bytes[0]==1&&(text.bytes[1]==2))
{
    printf("该系统为大端存储\n");
}
    else if(text.bytes[0]==2&&(text.bytes[1]==1))
{
    printf("该系统为小端存储\n");
}
   else
{
    printf("未知\n");
}
    return 0;
}

运行结果: 

        程序中用到联合体,联合体又称共用体,是指在同一段内存单元中存放不同类型的变量,先将数据通过value存储,再通过数组取出来,使之上是同一块地址的数据。

三、字节序转换函数

        当格式化的数据在两台使用不同字节序的主机之间直接传递时,接收端必然错误的接收。

        解决问题的方法是:发送端总是要把数据换成大端字节数据后在发送,而接收端知道对方传过来的数据总是以大端字节序,所以接收端可以根据自身采用的字节决定是否对接收到的数据进行转化(小端机转化,大端机不转化)。

        网络字节顺序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序采用大端排序方式。

#include <arpa/inet.h>
// 转换端口
uint16_t htons(uint16_t hostshort);
// 主机字节序 - 网络字节序(short类型,端口16位)

uint16_t ntohs(uint16_t netshort);
//网络字节序 - 主机字节序
// 转IP
uint32_t htonl(uint32_t hostlong);
// 主机字节序 - 网络字节序(long类型,IP32位)
uint32_t ntohl(uint32_t netlong);
// 主机字节序 - 网络字节序

 案例操作:

#include <stdio.h>
#include <arpa/inet.h>
int main()
{
           //htons: 转换端口;
           unsigned short a=0x0102;//两个字节,在计算机中存储,小端存储;
           printf("%x\n",a);//输出结果应该为01 02
           unsigned short b= htons(a);//转换为网络字节序,大端排序。
           printf("%x\n",b);//输出结果应该为02 01
           printf("-------------------------\n");
           //htonl:转化IP
           char buf[4]={192,168,1,100};//四个字节,在计算机中存储,小端存储;
           int n=* (int *)buf;
           int sum= htonl(n);
           unsigned char *p=(char *)&sum;
           printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));//输出结果应该为100 1 168 192
           printf("-------------------------\n");
           //ntohl:转IP
            unsigned char buf1[4]={1,1,168,192};//四个字节,网络字节序,大端排序;
            int n1=*(int*)buf1;
            int sum1=ntohl(n1);
            unsigned char *p1=(unsigned char *)&sum1;
            printf("%d %d %d %d\n",*p1,*(p1+1),*(p1+2),*(p1+3));//输出结果应该为 192 168 1 1
            printf("-------------------------\n");
           //ntohs:转换端口;
           unsigned short a1=0x3412;//两个字节,在网络字节序,大端排序;
           printf("%x\n",a1);//输出结果应该为34 12
           unsigned short b1=ntohs(a1);//转化为计算机存储,小端存储;
           printf("%x\n",b1);//输出结果应该为12 34
           
    return 0;
}

运行结果: 

四、socket地址

       客户端访问——>服务器;服务器需要告诉客户端IP、端口号;

        socket是一个结构体,用来封装端口号和IP等信息。

        后面socket相关的api中需要用到socket地址;

 socket 网络编程接口中表示 socket 地址的是结构体 sockaddr;

#include <bits/socket.h>
typedef unsigned short int sa_family_t;

struct sockaddr {
sa_family_t sa_family;
char  sa_data[14];
};

        sa_family 成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。常见的协议族(protocol family,也称 domain)和对应的地址族入下所示:

        sa_data 成员用于存放 socket 地址值。但是,不同的协议族的地址值具有不同的含义和长度,如下所示:

        由上表可知,14 字节的 sa_data 根本无法容纳多数协议族的地址值。因此,Linux 定义了下面这个新的通用的 socket 地址结构体,这个结构体不仅提供了足够大的空间用于存放地址值,而且是内存对齐的。 

上面的结构体是给IPV4用的,后面的这个才是为IPV6应用;

#include <bits/socket.h>
struct sockaddr_storage
{
sa_family_t sa_family;
unsigned long int __ss_align;
char __ss_padding[ 128 - sizeof(__ss_align) ];
};
typedef unsigned short int sa_family_t;

         __ss_padding于存放 socket 地址值,包括IPV6的端口号,流标识,地址,范围ID等;

专用 socket 地址

        很多网络编程函数诞生早于 IPv4 协议,那时候都使用的是 struct sockaddr 结构体,为了向前兼容,现在sockaddr 退化成了(void *)的作用,传递一个地址给函数,至于这个函数是sockaddr_in 还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型。

#include <netinet/in.h>
struct sockaddr_in
{
   sa_family_t sin_family;
/* __SOCKADDR_COMMON(sin_) */
   in_port_t sin_port;         /* Port number. */
   struct in_addr sin_addr;    /* Internet address. */
   /* Pad to size of `struct sockaddr'. */
   unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE -
              sizeof (in_port_t) - sizeof (struct in_addr)];
};  
struct in_addr
{
   in_addr_t s_addr;
};
struct sockaddr_in6
{
   sa_family_t sin6_family;
   in_port_t sin6_port;
/* Transport layer port # */
   uint32_t sin6_flowinfo; /* IPv6 flow information */
   struct in6_addr sin6_addr; /* IPv6 address */
   uint32_t sin6_scope_id; /* IPv6 scope-id */
};
typedef unsigned short  uint16_t;
typedef unsigned int    uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))

         所有专用 socket 地址(以及 sockaddr_storage)类型的变量在实际使用时都需要转化为通用 socket 地址类型 sockaddr(强制转化即可),因为所有 socket 编程接口使用的地址参数类型都是 sockaddr。

五、IP地址转换

        通常,人们习惯可读性好的字符串表示IP地址,比如用点分十进制字符串表示IPV4地址,以及用十六进制数字串表示IPV6地址。但是编程的时候,我们需要先把它们转化为整数(二进制数)方便实用,而记录日志时则相反,我们要把整数表示的IP地址转化为可读的字符串。下面3个函数可用于点分十进制字符串表示的IPV4地址和网络字节序整数表示的IPV4地址之间的转换。

1、将字符串的IP转化成整数;

2、主机、网络字节序的转换。

        下面的只适合IPV4的函数操作: 

 #include <arpa/inet.h>
        in_addr_t inet_addr(const char *cp);
        int inet_aton(const char *cp, struct in_addr *inp);
        char *inet_ntoa(struct in_addr in);

        下面这对更新的函数也能完成前面 3 个函数同样的功能,并且它们同时适用 IPv4 地址和 IPv6 地址: 

#include <arpa/inet.h>
// p:点分十进制的IP字符串,n:表示network,网络字节序的整数
int inet_pton(int af, const char *src, void *dst);
af:地址族: AF_INET  AF_INET6
   src:需要转换的点分十进制的IP字符串
   dst:转换后的结果保存在这个里面
// 将网络字节序的整数,转换成点分十进制的IP地址字符串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
af:地址族: AF_INET  AF_INET6
   src: 要转换的ip的整数的地址
   dst: 转换成IP地址字符串保存的地方
   size:第三个参数的大小(数组的大小)
返回值:返回转换后的数据的地址(字符串),和 dst 是一样的

#include <arpa/inet.h>
#include <stdio.h>

int main()
{
    //将点分十进制的IP字符串转化为网络字节序的整数;
    //创建一个IP字符串;
    char buff[]="192.168.1.4";//点分十进制字符串;
    unsigned int num=0;
    inet_pton(AF_INET,buff,&num);
    unsigned char *p=(unsigned char *)(&num);
    printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));
    printf("------------------------\n");

    //将网络字节的IP整数转化为十进制的字符串;
     char buff1[16];
     inet_ntop(AF_INET, &num, buff1, 16);
     printf("%s\n",buff1);
    return 0;
}

 六、TCP通信流程

TCP与UDP:

        都是传输层协议;

UDP:用户数据报协议,面向无连接,可以单播,多播,广播;面向数据报,不可靠。

TCP:传输控制协议,面向连接的,可靠的,基于字节流,仅支持单播传输。        

UDPTCP
是否创建连接无连接面向连接
是否可靠不可靠可靠的
连接的对象个数一对一、一对多、多对一、多对多支持一对一
传输方式面向数据报面向字节流
首部开销8字节

最少20字节

适应场景实时应用可靠性高的应用

 TCP通信流程:

服务器端(被动接收连接的角色)

1、创建一个 用于监听的套接字(fd);

        监听:监听有客户端的连接;

        套接字:实质就是一个文件描述符;

2、将这个监听文件描述符和本地的IP和端口绑定(IP和端口就是服务器的地址信息);

        客户端连接服务器的时候使用的就是这个IP和端口。

3、设置监听,监听的fd开始工作。

4、阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个和客户端通信的套接字(fd)。

5、通信

        -接收数据

        -发送数据

6、通信结束,断开连接。

客户端

1、创建一个用于通信的套接字(fd)

2、连接服务器,需要指定连接的服务器的IP和端口;

3、连接成功了,客户端直接与服务器通信。

        -接收数据

        -发送数据

4、通信结束,断开连接。

七、socket函数

涉及头文件:

        #include <sys/types.h>      
        #include <sys/socket.h>
        #include <arpa/inet.h> // 包含了这个头文件,上面两个就可以省略;

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

功能:

        创建一个套接字;

参数:

        domain:协议族;

                AF_INET : ipv4  
                AF_INET6 : ipv6     
                AF_UNIX, AF_LOCAL : 本地套接字通信(进程间通信)

        type:通信过程中使用的协议类型

                SOCK_STREAM : 流式协议
                SOCK_DGRAM : 报式协议

        protocol:具体的一个协议。一般写0;

                如果参数2写的是SOCK_STREAM:流式协议默认使用TCP;

                如果参数2写的是SOCK_DGRAM:报式协议默认是UDP;

返回值:

        成功,返回文件描述符,操作的就是内核缓冲区;

        失败:-1;               

 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能:

        绑定,将fd和本地的IP+端口号进行绑定;

参数:

        sockfd:通过socket函数获得的文件描述符;

        addr:需要绑定的sockrt地址,这个地址封装了ip和端口号的信息;

        addrlen:第二个参数结构体占的内存大小;

返回值:

        成功:0;

        失败:-1;

 int listen(int sockfd, int backlog);

功能:

        监听这个 socket上的连接;

参数:

        sockfd:通过socket函数获得的文件描述符;

        backlog:未连接和已经连接的和最大值;5;

返回值:

        成功:0;

        失败:-1;

 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 功能

        接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接。
 参数:   
           sockfd : 用于监听的文件描述符
           addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
           addrlen : 指定第二个参数的对应的内存大小
  返回值:
           成功 :用于通信的文件描述符
           失败 :  1 。

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

 功能: 客户端连接服务器
 参数:
            sockfd : 用于通信的文件描述符
            addr : 客户端要连接的服务器的地址信息
           addrlen : 第二个参数的内存大小
返回值:

                成功: 0

                失败 :-1

ssize_t write(int fd, const void *buf, size_t count);// 写数据
ssize_t read(int fd, void *buf, size_t count);// 读数据

八、TCP通信实现      

8.1服务器端    

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    // 1、创建用于监听的套接字
   int lfd = socket(AF_INET, SOCK_STREAM,0 );
   if(lfd==-1)
   {
        perror("socket");
       exit(-1);
   }

    // 2、绑定
   struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    //inet_pton(AF_INET,"192.168.193.128", saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr=INADDR_ANY;
    saddr.sin_port=htons(9999);
    int ret=bind(lfd,(const struct sockaddr *)&saddr,sizeof(saddr));
    if(ret==-1)
   {
        perror("socket");
        exit(-1);
   }
    //3、监听
   ret = listen(lfd,8);
   if(ret==-1)
   {
        perror("listen");
       exit(-1);
   }

   //4、接收客户端连接
     struct sockaddr_in clientadder;
     socklen_t len=sizeof(clientadder); 
   int cfd=accept(lfd,(struct sockaddr *)&clientadder, &len);
   if(cfd==-1)
   {
      perror("accept");
       exit(-1);
   }
//输出客户端信息
     char clinetIP[16];
     inet_ntop(AF_INET, &clientadder.sin_addr.s_addr,clinetIP,sizeof(clinetIP));
     unsigned short clientport=ntohs(clientadder.sin_port);
     printf("IP:%s\n",clinetIP);
     printf("Port:%d\n",clientport);
     

//5、通信
//获取客户端数据
char recivebuff[1024]={0};
int num =read(cfd, recivebuff, sizeof(recivebuff));// 写数据
if(num==-1)
{
perror("read");
       exit(-1);
}else if(num>0)
{
     printf("recive client data:%s\n",recivebuff);

}
else if(num==0)
{
//表示客户端断开连接。
printf("clinet closed...\n");
}
//给客户端发送信息
char *data="hello,1234!";
 write(cfd, data, strlen(data));// 读数据
 //关闭文件描述符
     close(cfd);
     close(lfd);

    return 0;
}

运行结果: 

 8.2客户端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    //1、创建套接字
  int fd = socket(AF_INET,SOCK_STREAM,0);
  if(fd==-1)
  {
    perror("socket");
    exit(-1);
  }
    //2、连接服务器端
    struct sockaddr_in serve_addr;
    serve_addr.sin_family=AF_INET;
   inet_pton(AF_INET,"192.168.254.170",&serve_addr.sin_addr.s_addr);
   serve_addr.sin_port=htons(9999);
   int ret= connect(fd,(const struct sockaddr *)&serve_addr,sizeof(serve_addr));
    if(ret==-1)
    {
        perror("connect");
        exit(-1);
    }
    //3、进行通信
    char * data="I am client\n";
    write(fd, data, strlen(data));

    char readbuff[1024]={0};
    int len =read(fd,readbuff,sizeof(readbuff));
    if(len==-1)
{
perror("read");
       exit(-1);
}else if(len>0)
{
     printf("recive client data:%s\n",readbuff);

}
else if(len==0)
{
//表示服务器端断开连接。
printf("serve closed...\n");
}
//关闭文件描述符;
  close(fd);
    return 0;

}

运行结果: 

上面的代码是,服务器与客户端只能一次通信,各自发送提前准备好的数据;

下面的代码是多次通信,而且客户端的数据由键盘输入,服务器收到什么数据,就发送什么数据。

服务器:

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    // 1、创建用于监听的套接字
   int lfd = socket(AF_INET, SOCK_STREAM,0 );
   if(lfd==-1)
   {
        perror("socket");
       exit(-1);
   }

    // 2、绑定
   struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    //inet_pton(AF_INET,"192.168.193.128", saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr=INADDR_ANY;
    saddr.sin_port=htons(9999);
    int ret=bind(lfd,(const struct sockaddr *)&saddr,sizeof(saddr));
    if(ret==-1)
   {
        perror("socket");
        exit(-1);
   }
    //3、监听
   ret = listen(lfd,8);
   if(ret==-1)
   {
        perror("listen");
       exit(-1);
   }

   //4、接收客户端连接
     struct sockaddr_in clientadder;
     socklen_t len=sizeof(clientadder); 
   int cfd=accept(lfd,(struct sockaddr *)&clientadder, &len);
   if(cfd==-1)
   {
      perror("accept");
       exit(-1);
   }
//输出客户端信息
     char clinetIP[16];
     inet_ntop(AF_INET, &clientadder.sin_addr.s_addr,clinetIP,sizeof(clinetIP));
     unsigned short clientport=ntohs(clientadder.sin_port);
     printf("IP:%s\n",clinetIP);
     printf("Port:%d\n",clientport);
     

//5、通信
//获取客户端数据
 char recivebuff[1024]={0};
while(1){
     int num =read(cfd, recivebuff, sizeof(recivebuff));// 读数据
     if(num==-1)
     {
     perror("read");
       exit(-1);
     }else if(num>0)
     {
          printf("recive client data:%s\n",recivebuff);

     }
     else if(num==0)
     {
     //表示客户端断开连接。
     printf("clinet closed...\n");
     break;
     }
     //给客户端发送信息
      write(cfd, recivebuff, sizeof(recivebuff));// 写数据

    // char *data="hello,1234!";
    //  write(cfd, data, strlen(data));// 读数据
}

 //关闭文件描述符
     close(cfd);
     close(lfd);

    return 0;
}

客户端 :


#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    //1、创建套接字
  int fd = socket(AF_INET,SOCK_STREAM,0);
  if(fd==-1)
  {
    perror("socket");
    exit(-1);
  }
    //2、连接服务器端
    struct sockaddr_in serve_addr;
    serve_addr.sin_family=AF_INET;
   inet_pton(AF_INET,"192.168.254.170",&serve_addr.sin_addr.s_addr);
   serve_addr.sin_port=htons(9999);
   int ret= connect(fd,(const struct sockaddr *)&serve_addr,sizeof(serve_addr));
    if(ret==-1)
    {
        perror("connect");
        exit(-1);
    }
    //3、进行通信
    char readbuff[1024]={0};
    char writebuff[1024]={0};
    while(1)
    {
      memset(writebuff,0,sizeof(writebuff));
      printf("请输入内容:\n");
      scanf("%s",writebuff);
       //char * data="I am client";
    write(fd, writebuff, sizeof(writebuff));
      sleep(1);

    int len =read(fd,readbuff,sizeof(readbuff));
      if(len==-1)
    {
        perror("read");
       exit(-1);
    }else if(len>0)
    {
     printf("recive client data:%s\n",readbuff);
    }
  else if(len==0)
  {
    //表示服务器端断开连接。
    printf("serve closed...\n");
    break;
  }
    }
 
//关闭文件描述符;
  close(fd);
    return 0;

}

 运行结果:

客户端:

服务器 :

        以上是利用socket实现了TCP通信的建立,并实现了客户端与服务器之间进行的数据的收发。

        创作不易,感谢大家多多支持!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1948300.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity】关于Luban的简单使用

最近看了下Luban导出Excel数据的方式&#xff0c;来记录下 【Unity】关于Luban的简单使用 安装Luban开始使用UnityLubanC# 扩展 安装Luban Luban文档&#xff1a;https://luban.doc.code-philosophy.com/docs/beginner/quickstart 1.安装dotnet sdk 8.0或更高版本sdk 2.githu…

windows安装redis设置密码、修改端口、提供外部访问

windows安装redis设置密码、修改端口、提供外部访问 一、前言1. 设置密码2. 修改端口3. 允许外部访问4. 注意事项 一、前言 设置Redis在Windows上设置密码、修改端口以及允许外部访问&#xff0c;需要进行以下步骤&#xff1a; 下载地址 https://github.com/tporadowski/redi…

unity2D游戏开发01项目搭建

1新建项目 选择2d模板,设置项目名称和存储位置 在Hierarchy面板右击&#xff0c;create Empty 添加组件 在Project视图中右键新建文件夹 将图片资源拖进来&#xff08;图片资源在我的下载里面&#xff09; 点击Player 修改属性&#xff0c;修好如下 点击Sprite Editor 选择第二…

机器人开源调度系统OpenTcs6二开-车辆表定义

前面已经知道opentcs 需要车辆的模型结构数据&#xff0c;将里面的数据结构化&#xff0c;已表的形式生成&#xff0c;再找一个开源的基础框架项目对车辆进行增删改的管理 表结构&#xff1a; CREATE TABLE Vehicle (id INT AUTO_INCREMENT PRIMARY KEY COMMENT 唯一标识符,n…

尝试带你理解 - 进程地址空间,写时拷贝

序言 在上一篇文章 进程概念以及进程状态&#xff0c;我们提到了 fork 函数&#xff0c;该函数可以帮我们创建一个子进程。在使用 fork 函数时&#xff0c;我们会发现一些奇怪的现象&#xff0c;举个栗子&#xff1a; 1 #include <stdio.h>2 #include <unistd.h>3 …

unity美术资源优化(资源冗余,主界面图集过多)

图片资源冗余&#xff1a; UPR unity的性能优化工具检查资源 1.检查纹理读/写标记 开启纹理资源的读/写标志会导致双倍的内存占用 检查Inspector -> Advanced -> Read/Write Enabled选项 2.检查纹理资源alpha通道 如果纹理的alpha通道全部为0&#xff0c;或者全部为2…

了解Selenium中的WebElement

Selenium中到处都使用WebElement来执行各种操作。什么是WebElement&#xff1f;这篇文章将详细讨论WebElement。 Selenium中的WebElement是一个表示网站HTML元素的Java接口。HTML元素包含一个开始标记和一个结束标记&#xff0c;内容位于这两个标记之间。 HTML元素的重命名 …

C++图网结构算法

目录 一.迪杰斯特拉算法&#xff08;dijkstra&#xff09; 1.实现原理&#xff1a; 2.代码实现&#xff1a; 3.例题&#xff1a; 二.spfa算法&#xff1a; 1.实现原理&#xff1a; 2.代码实现&#xff1a; 3.例题&#xff1a; 三.贝尔曼福特&#xff08;bellman_ford&…

HTTP模块(二)

HTTP 设置 HTTP 响应报文 HTTP报文常见属性&#xff1a; const http require(http);const server http.createServer((request, response) > {// 设置请求状态码 2xx 4xx 5xxresponse.statusCode 200;// 设置请求描述 了解即可response.statusMessage hello// 指定响…

Nodepad++运行Python文件的方法

windows中使用nodepad运行python文件 首先&#xff0c;需要在windows中安装python解释器。 然后打开nodepad&#xff0c;新建一个文件&#xff0c;输入一段测试的python代码 import socketdef get_hostname():try:# 获取主机名hostname socket.gethostname()return hostna…

uniapp集成安卓原生录屏插件以及使用

概述 我们知道UniApp的出现简化了开发者的工作流程&#xff0c;并减少了代码的重复编写。开发者可以使用一套代码编译到iOS、Android、以及各种小程序的应用&#xff0c;节省了人力和时间成本&#xff0c;但是涉及到与系统交互的时候&#xff0c;比如录屏、录音、录像、文件操…

Go语言常见序列化协议全面对比

先说结论 从易用性、性能、内存占用、编码后大小等几个方面综合考虑 ProtoBuf 胜出。 Gob 从性能和 I/O 带宽占用上都和 ProtoBuf 差不多&#xff0c;唯一劣势是编解码时内存占用较多。考虑到不用再写 IDL 带来的易用性&#xff0c;如果整个系统内不存在使用除 Go 以外其他语言…

『Django』搭建你的博客网站

theme: smartblue 点赞 关注 收藏 学会了 本文简介 如果你学了我前面写的 Django 文章&#xff0c;现在已经有能力去试试自己搭建博客网站了。 虽然用的不是现在流行的前后端分离的方式(前后端分离的方式会在之后的文章讲解)。 但在搭建网站之前我们还要做一些额外的功能让你…

【机器学习】梯度下降的基本概念和如何使用梯度下降自动化优化w和b

引言 梯度下降是一种用于寻找函数最小值的优化算法&#xff0c;它在机器学习中广泛用于训练模型&#xff0c;如线性回归、神经网络等 一、梯度下降的基本概念 1.1 目标函数 在机器学习中&#xff0c;这通常是损失函数&#xff08;如均方误差、交叉熵等&#xff09;&#xff0…

[渗透测试] 主动信息收集

主动信息收集 在红蓝对抗过程中&#xff0c;资产属于核心地位&#xff0c;攻击方&#xff08;红方&#xff09;要尽可能的去获取对方资产&#xff0c;暴露目标资产&#xff0c;包括IP地址、网络设备、安全设备、服务器、存储在服务器中的数据等。防守方也要清楚自己有多少有价…

了解网络是如何运作

“Web 的工作原理”提供了一个简化的视图,用于了解在计算机或手机上的 Web 浏览器中查看网页时发生的情况。 这个理论对于短期内编写 Web 代码来说并不是必需的,但不久之后,你就会真正开始从理解后台发生的事情中受益。 客户端和服务器 连接到 Internet 的计算机称为客户端和…

dns逆向解析,主从服务,多域名访问(穿插ntp服务器)

复习 域名解析&#xff1a; 正向解析&#xff1a;将域名解析为ip 反向解析&#xff1a;将ip解析为域名 逆向解析 关闭防火墙和selinux&#xff0c;配置静态ip [rootdns ~]# vim /etc/named.rfc1912.zones [rootdns ~]# vim /etc/named.conf [rootdns ~]# cd /var/named/ [rootd…

刚购买的阿里云服务器该如何配置环境(CentOS)

文章目录 购买开始初始设置登录云服务器安装 Apache 服务安装 MySQL安装 PHP快照 第三方 SSH 登录笔者的话 购买 按照需求购买就行。学生有免费试用一个月的活动&#xff0c;可以试着玩玩。 开始初始设置 登录云服务器 购买完后&#xff0c;点击实例&#xff0c;点击实例名…

Linux下RDMA驱动程序探索系列-2

本系列文章将带领读者逐步了解Linux操作系统下的RDMA子系统。本篇文章作为系列的第二篇&#xff0c;将深入内核态驱动程序的代码&#xff0c;主要介绍如下内容&#xff1a; Driver的初始化流程几个重要verbs回调函数的简介 01、Kernel Driver的初始化流程 由于不同厂商的驱动…

进销存系统开发,含税小计和含税单价计算,含税和不含税,1000元电脑为案例

if (data ! null) {console.log("中断调试&#xff0c;2024-7-25 最终计算税务");//删除不需要会报错var 未来之窗_人工智能_计算_税额 parseFloat((data.price * data.num * data.tax_rate / 100 ).toFixed(2));var 未来之窗_人工智能_计算_含税小计 parseFloat((…