TCP/IP四层模型


UDP/TCP协议

TCP(The Transmission Control Protocol):传输控制协议

UDP/TCP协议都属于传输层协议,都位于IP协议以上,将UDP/TCP数据报封装于IP数据内传输。

UDP首部:包含源端口,目标端口等数据。端口保证数据能准确传输到指定的进程。

IP协议是不可靠协议,UDP本身没有任何确保可靠的措施,故UDP协议也是不可靠协议。UDP协议适用于对数据可靠性,顺序交付无要求的程序,UDP能提供更快,更小消耗的传输服务。

TCP首部:相比UDP,TCP同样包含源端口,目标端口等数据的同时,TCP还包含序号,确认号等信息,这些数据用于确认数据是否被完整交付,TCP是可靠协议的原因就在这里。

TCP虽然建立在不可靠协议IP之上,但TCP采用了多种机制,确保数据有序,可靠的交付。TCP多用于对数据安全要求较高的应用,如Web,电子邮件等。

Socket编程

套接字的概念

套接字,是操作系统内核中的一个数据结构。它是网络中的节点进行相互通信的门户。它是网络进程的ID。

网络通信,归根究竟还是进程间的通信(不同计算机上的进程间通信)。在网络中。每个节点(计算机或路由)都有一个网络地址。也就是IP地址。

两个进程通信时,首先要确定各自所在的网络节点的网络地址。可是,网络地址仅仅能确定进程所在的计算机,而一台计算机上非常可能同一时候执行着多个进程,所以仅凭网络地址还不能确定究竟是和网络中的哪一个进程进行通信。因此套接口中还须要包含其它的信息。也就是port号(PORT)。在一台计算机中,一个port号一次仅仅能分配给一个进程,也就是说,在一台计算机中,port号和进程之间是一一相应关系。

所以,使用port号和网络地址的组合能够唯一的确定整个网络中的一个网络进程。

​ 网络编程也称为socket编程,socket通常译作”套接字“,为一套网络编程的接口API,但原意其实意译应该为”接口“。也就是操作系统提供给开发人员进行网络开发的API接口。这套接口通常可以通过参数的调整支持多种协议,包括TCP、UDP和IP等等。

​ 从编码角度看,源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务。

流套接字(SOCK_STREAM)------>使用TCP协议,能够实现可靠的数据服务,提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。

数据报套接字(SOCK_DGRAM)------>使用UDP协议,提供无连接的服务,无法保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。

原始套接字(SOCK_RAW)------>允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW。

应用编程接口即:调用一些操作系统提供的api,来与应用进程进行交互。而socket api是一套与网络通讯相关的、操作系统提供给应用开发的编程接口,如:微软公司在其操作系统中采用了套接字接口 API ,形成了一个稍有不同的 API,并称之为Windows Socket Interface或WINSOCK。

  • WSAStartup函数:

使用Socket的应用程序在使用Socket之前必须首先调用 WSAStartup函数,两个参数:

第一个参数指明程序请求使用的WinSock版本,其中高位字节指明副版本、低位字节指明主版本.十六进制整数,例如0x102表示2.1版。

第二个参数指向WSADATA结构的指针

返回实际的WinSock的版本信息。

  • WSACleanup函数:int WSACleanup(void);

​ 应用程序在完成对请求的Socket库的使用,最后要调用WSACleanup函数解除与Socket库的绑定并释放Socket库所占用的系统资源 。不清理的话,在运行codeblocks的时候,常常可能会发生id.exe被占用错误。无法运行,需要去管理器找进程关掉才可以继续运行。

  • socket函数:sd = socket(protofamily,type,proto);

    创建套接字,操作系统返回套接字描述符(sd)

    第一个参数(协议族): protofamily = PF_INET(TCP/IP)

    第二个参数(套接字类型): type = SOCK_STREAM,SOCK_DGRAM or SOCK_RAW(TCP/IP)

    第三个参数(协议号):0为默认

TCP通讯实例

1、首先,Winsock API 函数由WS2_32.DLL支持,可通过WS2_32.LIB访问。故Windows socket编程前需要加载ws2_32.lib,然后初始化WS2_32.DLL,通过函数WSAStartup完成初始化。一般程序最后需要终止DLL使用,此时需要调用WSACleanup函数。

ws2_32.dll是Windows Sockets应用程序接口, 用于支持Internet和网络应用程序。

DLL 是一个库,其中包含可同时由多个程序使用的代码和数据。对于Windows,操作系统的很多功能都由 DLL 提供。

//以下是加载库,以及初始化
#include<stdio.h>
#include<WinSock2.h>
#pragma comment (lib,"ws2_32.lib");           //加载ws2_32.libint main()
{//1、ws2_32.DLL初始化WSADATA wsaData;/** socket编程中:声明调用不同的Winsock版本。例如MAKEWORD(2,2)就是调用2.2版,MAKEWORD(1,1)就是调用1.1版。*/WSAStartup(MAKEWORD(2,2),&wsaData); //初始化/**中间代码*/WSACleanup();   //终止DLL使用
}

2、随后,创建socket套接字。

 //2、创建套接字/**    typedef    UINT_PTR        SOCKET;* typedef    unsigned int    UINT_PTR;*   在32位操作系统里,一个unsigned int是4个字节。64位操作系统上,一个unsigned int是8个字节。所以用UINT_PTR代替int理论上可以让代码具有更好的移植性,当然也让代码看起来更专业.* socket(int domain, int type, int protocol);* int domain参数表示套接字要使用的协议簇,网络编程一般使用AF_INET宏,AF_INET(TCP/IP – IPv4)* type参数指的是套接字类型,SOCK_STREAM(TCP流),SOCK_DGRAM(UDP数据报)* protocol表示协议,使用AF_INET簇,TCP连接时,设为IPPROTO_TCP。*/SOCKET serverSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

3、创建套接字之后,绑定套接字.

​ Socket套接字基于计算机网络,提供同一系统上不同进程或由局域网连接在一起的不同机器上的进程间通讯功能。如下图:

​ 套接字通过IP地址、Port端口号标识,通过这个标识可以在整个局域网定位一个套接字,通过套接字进程便可以相互传输数据。而这就是绑定套接字所做的工作,即为每一个套接字绑定相应的IP地址和端口号标识。

 //3、绑定套接字/** struct sockaddr_in这个结构体用来处理网络通信的地址。在各种系统调用或者函数中,只要和网络地址打交道,就得用到这两个结构体。网络       中的地址包含3个方面的属性:1 地址类型; 2 ip地址; 3 端口*/SOCKADDR_IN sockAddr;/** memset 函数是内存赋值函数,用来给某一块内存空间进行赋值的。(对一片内存空间逐字节进行初始化) * 其原型是:void* memset(void *_Dst, int  _Val, size_t _Size)* _Dst是目标起始地址,_Val是要赋的值,_Size是要赋值的字节数*/memset(&sockAddr,0,sizeof(sockAddr));  //初始化sockAddr.sin_family = AF_INET;   //地址类型sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");     //IP地址(127.0.0.1是回送地址,指本机)sockAddr.sin_port = htons(9080);    //端口号  htons函数:将主机字节顺序转化为网络字节顺序bind(serverSock,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));

​ 关于SOCKADDR_IN和SOCKADDR在另外文档中有详解。

bind函数将socket与协议、IP和端口号绑定起来,相对于给socket“命名”唯一的标识,这样其他的进程就可以通过这个标识找到这个socket。

//关于bind函数
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//函数作用:将本地地址与套接字关联起来。
//函数参数:
//   (1)参数 sockfd ,需要绑定的socket。
//  (2)参数 addr ,存放了服务端用于通信的地址和端口。
//  (3)参数 addrlen ,表示 addr 结构体的大小
//返回值:成功则返回0 ,失败返回-1,错误原因存于 errno 中。如果绑定的地址错误,或者端口已被占用,bind 函数一定会报错,否则一般不会返回错误。//关于inet_addr函数
unsigned long WSAAPI inet_addr(const char *cp);
//功能:inet_addr函数将包含 IPv4 点分十进制地址的字符串转换为 IN_ADDR 结构的 正确 地址
//参数:cp代表点分十进制的IP地址,如1.2.3.4
//返回值:
/*如果没有发生错误,inet_addr函数将返回一个无符号长整型值,其中包含给定 Internet 地址的合适二进制表示。如果cp参数中的字符串不包含合法的 Internet 地址,例如,如果“abcd”地址的一部分超过 255,则 inet_addr返回值INADDR_NONE。
*/

4、进入监听状态

//4、进入监听状态listen(serverSock,20);

int listen(SOCKET s, int nQueueSize);

第一个参数: 监听的socket。

第二个参数: 套接字监听队列最大连接请求数。

该函数将监听对socket的连接请求

5、

 //5、定义接受客户端请求的数据结构变量SOCKADDR cltAddr;int nSize = sizeof(cltAddr);//buffer是一块共用的内存,刚开始接收的数据放在这里面,给客户端回复消息也用的这块内存char buffer[BUF_SIZE] = { 0 };while (1){//6、阻塞直到客户端发来消息,创建客户socketSOCKET clientSock = accept(serverSock,(SOCKADDR*)&cltAddr,&nSize);//7、接受客户端数据int strLen = recv(clientSock, buffer, BUF_SIZE, 0);  //数据存放在buffer这个字符串数组中printf("Message form client: %s\n", buffer);//8、给客户端回复消息printf("Input a string:\n");getss(buffer,BUF_SIZE);send(clientSock, buffer, strLen, 0);//9、关闭客户端套接字closesocket(clientSock);//10、重置缓冲区memset(buffer,0,BUF_SIZE);}
  • 接受连接请求

SOCKET accept(SOCKET s, struct sockaddr *addr, int *addrlen);

第一个参数: socket为被监听的socket,即服务端socket

第二个参数: 对应AF_INET,一个sockaddr指针,将写入发送请求方的sockaddr_in信息,即客户端的sockaddr_in信息。

第三个参数: 对应AF_INET,sockaddr结构体的大小。

该函数用于接受一个socket连接请求,返回一个新的连接socket(可以理解为客户端的socket),发送与接收数据通过这个连接socket。

  • 发送数据

int send(SOCKET s, const char *buf, int len, int flags );

第一个参数: socket为对方的socket。

第二个参数: 发送数据的缓冲区。

第三个参数: 数据缓冲区大小。

第四个参数: 紧急状态,一般值为0。

该函数用于向对方socket发送数据,成功返回发送数据的大小数

  • 接受数据

int recv(SOCKET s, char *buf, int len, int flags);

第一个参数: socket为对方的socket。

第二个参数: 接收数据的缓冲区。

第三个参数: 缓冲区大小。

第四个参数: 紧急状态,一般为0。

该函数用于接收对方发送的数据,成功返回发送数据的大小数

最后关闭服务端套接字、终止DLL使用即可

服务器端的源码:

#include<stdio.h>
#include<WinSock2.h>
#pragma comment (lib,"ws2_32.lib");           //加载ws2_32.lib
#pragma warning (disable:4996);         //解决C4996 'inet_addr'错误
#define BUF_SIZE 100/*
* char* getss(char* str, int num)
{if (fgets(str, num, stdin) != 0){//size_t是标准C库中定义的,在64位系统中为long long unsigned intsize_t len = strlen(str);if (len > 0 && str[len - 1] == 'n')str[len - 1] = ' ';return str;}
}
*/int main()
{//1、ws2_32.DLL初始化WSADATA wsaData;/** socket编程中:声明调用不同的Winsock版本。例如MAKEWORD(2,2)就是调用2.2版,MAKEWORD(1,1)就是调用1.1版。*/WSAStartup(MAKEWORD(2,2),&wsaData); //初始化//2、创建套接字/**socket(int domain, int type, int protocol);* 其中 “int domain”参数表示套接字要使用的协议簇  AF_INET(TCP/IP – IPv4)* “type”参数指的是套接字类型,SOCK_STREAM(TCP流),SOCK_DGRAM(UDP数据报)* protocol”一般设置为“0*/SOCKET serverSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//3、绑定套接字/** struct sockaddr_in这个结构体用来处理网络通信的地址。在各种系统调用或者函数中,只要和网络地址打交道,就得用到这两个结构体。网络中的地址包含3个方面的属性:1 地址类型; 2 ip地址; 3 端口*/SOCKADDR_IN sockAddr;/** memset 函数是内存赋值函数,用来给某一块内存空间进行赋值的。(对一片内存空间逐字节进行初始化) * 其原型是:void* memset(void *_Dst, int  _Val, size_t _Size)* _Dst是目标起始地址,_Val是要赋的值,_Size是要赋值的字节数*/memset(&sockAddr,0,sizeof(sockAddr));  //初始化sockAddr.sin_family = AF_INET;   //地址类型sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");     //IP地址(127.0.0.1是回送地址,指本机)sockAddr.sin_port = htons(9080);    //端口号  htons函数:将主机字节顺序转化为网络字节顺序bind(serverSock,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));//4、进入监听状态listen(serverSock,20);printf("服务器进入监听状态...\n");//5、定义接受客户端请求的数据结构变量SOCKADDR cltAddr;int nSize = sizeof(cltAddr);char buffer[BUF_SIZE] = { 0 };int num = 0;while (1){//6、阻塞直到客户端发来消息,创建客户socketSOCKET clientSock = accept(serverSock,(SOCKADDR*)&cltAddr,&nSize);if(!num)printf("连接成功\n");    //7、接受客户端数据int strLen = recv(clientSock, buffer, BUF_SIZE, 0);printf("Message form client: %s\n", buffer);//8、给客户端回复消息printf("Input a string to client:");//   getss(buffer, sizeof(buffer));gets(buffer);send(clientSock, buffer, strLen, 0);//9、关闭客户端套接字closesocket(clientSock);//10、重置缓冲区memset(buffer,0,BUF_SIZE);num++;}//11、关闭服务端套接字closesocket(serverSock);//12、终止DLL使用WSACleanup();
}

注:目前GCC中还没有完全实现此标准, 因此 gets_s() 函数尚未包含在目前的GNU 工具链中,因此调用gets_s函数出错,这里干脆写了一个getss函数。

这是老师写的getss函数,我感觉直接用gets函数都可以。

关于fgets函数:

char *fgets(char *buf, int bufsize, FILE *stream);
参数
*buf: 字符型指针,指向用来存储所得数据的地址。
bufsize: 整型数据,指明存储数据的大小。
*stream: 文件结构体指针,将要读取的文件流。
返回值

成功,则返回第一个参数buf;

在读字符时遇到end-of-file,则eof指示器被设置,如果还没读入任何字符就遇到这种情况,则buf保持原来的内容,返回NULL;
如果发生读入错误,error指示器被设置,返回NULL,buf的值可能被改变。

客户端跟服务端类似,源码如下:

#include<stdio.h>
#include<Winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)#define BUF_SIZE 100/*
char* getss(char* str, int num)
{if (fgets(str, num, stdin) != 0){size_t len = strlen(str);// if (len > 0 && str[len - 1] == 'n')//        str[len - 1] = ' ';return str;}return 0;
}
*/int main()
{//初始化DLLWSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);//创建套接字SOCKADDR_IN sockAddr;memset(&sockAddr, 0, sizeof(sockAddr));sockAddr.sin_family = AF_INET;sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");sockAddr.sin_port = htons(9080);char bufSend[BUF_SIZE] = { 0 };char bufRecv[BUF_SIZE] = { 0 };while (1){// 3、创建客户端套接字SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 4、连接服务器connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));//5、获取用户输入的字符串发送给服务器printf("Input a string:");gets(bufSend);//  getss(bufSend, sizeof(bufSend));send(sock, bufSend, BUF_SIZE, 0);//6、接受服务器传回来的消息recv(sock, bufRecv, BUF_SIZE, 0);printf("Message form server: %s\n",bufRecv);//7、重置缓冲区memset(bufSend, 0, BUF_SIZE);memset(bufRecv, 0, BUF_SIZE);// 8、关闭套接字closesocket(sock);              }// 9、终止DLL使用WSACleanup();return 0;
}

注意connect函数即可:

int connect(SOCKET s,const struct sockaddr *saddr,int namelen) ;

第一个参数: socket本地进程的socket。

第二个参数: 对应AF_INET,对方IP,端口等socket地址标识sockaddr_in。

第三个参数: 对应AF_INET,使用sockaddr_in结构大小。

补充知识

什么是pragma

#pragma指令的作用是:用于指定计算机或操作系统特定的编译器功能。C 和 C++ 的每个实现均支持某些对其主机或操作系统唯一的功能。

  • #pragma once

大家应该都知道:指定该文件在编译源代码文件时仅由编译器包含(打开)一次
使用 #pragma once 可减少生成次数,和使用预处理宏定义来避免多次包含文件的内容的效果是一样的,但是需要键入的代码少,可减少错误率。

  • #pragma waring(…)

    启用编译器警告消息的行为和选择性修改。

    #pragma warning( disable : 4507 34; once : 4385; error : 164 ) //这1行跟下面3行效果一样

    #pragma warning( disable : 4507 34 ) //不发出4507和34警告,即有4507和34警告时不显示
    #pragma warning( once : 4385 ) //4385警告信息只报告一次
    #pragma warning( error : 164 ) //把164警告信息作为一个错误

  • #pragma comment(comment-type [,“commentstring”])

    该指令将一个注释记录放入一个对象文件或可执行文件中。

    comment-type 是一个预定义的标识符(如下所述,一共5个),它指定了注释记录的类型。 可选 commentstring 是一个字符串,它提供了某些注释类型的附加信息。 由于 commentstring 是一个字符串,因此它遵循有关转义字符、嵌入的引号 (") 和串联的字符串的所有规则。

    • compiler
      将编译器的名称和版本号置于对象文件中。 此注释记录将被链接器忽略。 如果为此记录类型提供 commentstring 参数,则编译器会生成警告。

    • exestr
      将 commentstring 置于对象文件中。 在链接时,会将该字符串置于可执行文件内。 加载可执行文件时,不会将字符串加载到内存中;但是,可以使用在文件中查找可打印字符串的程序来找到它。 此注释记录类型的一个用途是将版本号或类似信息嵌入可执行文件中。

    • linker
      将链接器选项置于对象文件中。 可以使用注释类型来指定链接器选项,而不是将其传递到命令行或在开发环境中指定它。

    • user
      将一般注释置于对象文件中。 commentstring 参数包含注释文本。 此注释记录将被链接器忽略。

    • lib(这个最常用了)
      将库搜索记录置于对象文件中。 此注释类型必须带有包含您希望链接器搜索的库的名称(和可能的路径)的 commentstring 参数。 库名称遵循对象文件中的默认库搜索记录;链接器会搜索此库,这就像在命令行上对其命名一样,前提是未使用 /nodefaultlib 指定库。 可以将多个库搜索记录置于同一个源文件中;各个记录将以其在源文件中显示的顺序出现在对象文件中。

      如果默认库和添加的库的顺序很重要,则使用 /Zl 开关进行编译会阻止将默认库名称置于对象模块中。 然后,可使用另一个注释指令在添加的库的后面插入默认库的名称。 与这些指令一起列出的库将以其在源代码中的发现顺序出现在对象模块中。

  • #pragma message(messageString)

    不中断编译的情况下,发送一个字符串文字量到标准输出。message编译指示的典型运用是在编译时显示信息。

MAKEWORD解释

​ MAKEWORD是将两个byte型合并成一个word型,一个在高8位(b),一个在低8位(a)

​ 比如a=2;b=1
​ 2的二进制是00000010,1的二进制为00000001,B是表示高8位,A表示低8位 合并起来就是100000010

Connect函数

int WSAAPI connect(

SOCKET s,

const struct sockaddr FAR * name,

int namelen

);

s

标识未连接套接字的描述符。

name

指向应建立连接的sockaddr结构的指针。

namelen

name参数指向的sockaddr结构的长度(以字节为单位)。

网络通讯学习(1)---TCP通讯相关推荐

  1. 嵌入式linux学习笔记--TCP通讯整理

    嵌入式linux学习笔记–TCP通讯整理 之前的项目中使用到了比较多的tcp 通讯相关的知识,一直也没有进行整理,今天准备拿出时间好好的整理一下TCP通讯的整个过程.预计会整理linux和window ...

  2. 网络编程学习笔记(TCP套接口选项)

    其套接口级别为IPPROTO_TCP TCP_KEEPALIVE: 指定TCP开始发送保持存活探测分节前以秒为单位的连接空闲时间.此选项在SO_KEEPALIVE套接口选项打开时才有效 TCP_MAX ...

  3. 网络编程学习笔记(TCP回射服务器程序修订版)

    服务器端: #include <sys/socket.h> #include <stdio.h> #include <netinet/in.h> #include ...

  4. Esp8266 进阶之路20 【高级篇】深入学习esp8266的esp now模式组网,仿机智云做一个小网关,实现无需网络下轻松彼此连接通讯交互数据。(附带Demo)

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,不做开发板.仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 序号 SDK版本 内容 链接 1 nonos2.0 搭建 ...

  5. 网络编程2_网络通讯协议, socket(tcp, udp)

    一. 网络通讯协议     互联网协议的功能: 定义计算机如何接入internet, 以及接入internet的计算机的通信标准     互联网协议按照功能的不同分为osi七层或tcp/ip五层    ...

  6. linux网络编程(二)TCP通讯状态

    linux网络编程(二)TCP通讯状态 TCP状态转换 为什么需要等待2MSL? 端口复用 TCP状态转换 tcp协议连接开始会经过三次握手,客户端和服务器开始都会处于CLOSED状态 第一次握手:客 ...

  7. 交换机 链路层无法udp通讯_一文读懂计算机底层网络原理,包括TCP、UDP、header,什么是包、帧、段等关键问题...

    说到计算机网络原理,大家可能马上联想到,七层协议,传输层,链路层,三次握手四次挥手:前端的同学,还会想到我们用Crome F12的network里面的headers,状态码等.后端同学可能会联想到,抓 ...

  8. UR5机器人学习之TCP/IP通讯

    最近因为项目原因在研究UR机器人,主要是通过PC上位机与UR通讯,采用TCP/IP通讯的方式,PC上位机通过发送UR脚本指令实现控制机器人和操作机器人. IP地址:可以根据UR机器人控制终端读取到IP ...

  9. JAVA通信编程(三)——TCP通讯

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

最新文章

  1. STM32-超级终端串口控制程序
  2. 给Linux系统/网络管理员的nmap的29个实用例子
  3. 利用飞信给自己发短信的shell脚本
  4. JavaScript装逼指南
  5. html5常用的属性标签,HTML5常用标签及其属性设置
  6. 深入理解RCU | RCU源码剖析
  7. metadata model entry in /IWFND/CL_MED_MDL_SVC_GRP
  8. qt调用opencv汇总(2)
  9. 微盟合作,重磅推出全免费的H5专业营销平台,快速创建第一个H5活动(捷微H5)
  10. fluent并行 linux_windows 系统下启动linux主机群的fluent并行操作.docx
  11. NetScaler SDWAN 的前世今生
  12. python插值算法实现_图像插值算法Opencv+python实现
  13. Git(2)-- Git安装后首次配置与第一次使用Git和Github管理自己的代码(超详细纯小白图文教程)
  14. 20144303 《Java程序设计》第一周学习总结
  15. Java 避免创建不必要的对象
  16. 华为服务器修改SN,华为光猫修改双模、SN序号、MAC地址方法
  17. java大写英文字母_输出一个字符串中的大写英文字母数,小写英文字母数以及非英文字母(.java)...
  18. 收集到一些关于python的文章,存起来慢慢看。。。
  19. Arduino串口控制DY-SV5W音频播放
  20. 创建三个学生对象,分别打印其详细信息

热门文章

  1. 抖音上面用计算机解锁手机,抖音生命计算器
  2. 【Kaggle从入门到放弃】(01):竞赛类型
  3. 程序员经典电子书下载(超全)
  4. 守着“伪需求”闭门造车,还谈什么大数据价值变现
  5. cps评分和tps评分_【一点资讯】一文总结:PD-1/PD-L1免疫检查点抑制剂和TPS、CPS、IPS...
  6. 【软考——系统架构师】信息系统基础
  7. 帷幄DAM - 直播营销引流 分析直播引流推广的技巧
  8. 浪潮信息m6服务器性能,M6四路服务器——浪潮信息新品性能显著
  9. 初始Android动画
  10. 计算机主机和cpu的区别,主机与cpu的区别