文章目录

  • 字节序
  • 套接字编程
  • 客户端与服务端
  • ★ netstat命令 ★
  • UDP通信程序
    • 通信流程:
    • 接口:
    • 流程外的重要接口:
    • 服务端代码
    • 客户端代码
  • TCP通信程序
    • 通信流程:
    • 接口:
    • 代码实例:
    • 出现问题:
    • 解决方案:
      • 优化后的代码

字节序

字节序:cpu对内存中数据进行存取的顺序

主机字节序的分类:小端、大端

小端:低地址存低位
大端:低地址存高位


编写代码判断主机字节序:

#include<iostream>
using namespace std;
void check_sys1()
{int a = 1;char* b = (char*)&a;if (*b == 1)cout << "小端" << endl;if (*b == 0)cout << "大端" << endl;
}
void check_sys2()
{//联合类型的所有数据共用一块内存,内存大小根据最大的数据类型决定union UN {int a;char b;}u;u.a = 1;if(u.b==1)cout << "小端" << endl;if(u.b==0)cout << "大端" << endl;
}
int main()
{check_sys1();check_sys2();return 0;
}

主机字节序对网络通信的影响:如果通信两端主机字节序不同,可能会造成数据二义性。

解决方案:订立网络通信字节序标准,规定网络中的数据都按照网络字节序进行存取。网络字节序-----其实是大端字节序

发送方将数据转换成网络字节序后进行发送,接收方根据自己的的主机字节序将接收到的数据进行转换。

字节序只针对存储单元大于一个字节的数据类型。
注意:字符串实际上是单字节存储,所以不需要转换。

套接字编程

socket 套接字编程:网络通信程序的编写

分类:UDP协议通信程序的/TCP协议通信程序

区别:
UDP协议:用户数据报协议
特点:无连接,不可靠,面向数据报
应用场景:实时性要求大于安全性要求----例如视频传输

TCP协议:传输控制协议
特点:面向连接,可靠,面向字节流
应用场景:安全性要求大于实时性要求----例如文件传输

客户端与服务端

在网络通信程序中,通信两端被分为客户端和服务端,
客户端:提供给客户的通信段,通常是通信程序中主动发起请求的一端。
客户端必须提前知道服务端的地址信息(ip地址+port端口)才能发送请求,通常是被提前写在应用程序中,并且通常是固定不变的。

服务端:通常是指被动接受请求,提供服务的通信端。

★ netstat命令 ★

netstat命令:查看当前网络状态信息
-a:查看所有
-t :查看TCP信息
-u:查看UDP信息
-n:不以服务名称显示,以具体地址端口显示
-p:查看当前网络状态对应的进程

UDP通信程序

通信流程:

接口:

1、创建套接字:int socket(int domain,int type,int protocol);

domain:地址域类型----指定使用的是什么样的地址结构:AF_INET----IPV4通信,使用IPV4地址结构
type:套接字类型;SOCK_STREAM:流式套接字 / SOCK_DGRAM:数据报套接字

注意:TCP协议必须使用SOCK_STREAM,UDP必须使用SOCK_DGRAM

protocol:本次通信所使用的协议;IPPROTO_IP=6 / IPPROTO_UDP=17(可以使用宏,也可以使用数字)
返回值:成功,返回一个文件描述符----操作句柄;失败,返回-1。

2、为套接字绑定地址信息:int bind(int sockfd,struct sockaddr* addr,socklen_t addrlen);

sockfd:socket() 创建套接字返回的操作句柄
addr:当前绑定的地址信息
socklen_t addrlen:地址信息长度
返回值:成功返回0;失败返回-1。

3、接收数据:ssize_t recvfrom(int sockfd,void* buf,int len,int flag,struct addr* srcaddr,socklen_t * addrlen);

ssize_t:有符号int;
size_t:无符号int

sockfd:创建套接字返回的操作句柄
buf:用于存放接收到的数据的空间地址
len:需要接受的数据长度
flag:选项标志,通常默认为0,表示阻塞接收
srcaddr:本条数据的源端地址信息
addrlen:输入输出参数,指定要接收多长的地址长度,但实际可能并没有那么长,所以还会返回实际接收到的地址长度
返回值:成功,返回实际接收到的数据长度;失败或出错返回-1。

4、发送数据:ssize_t sendto(int sockfd,void *data,int len,int flag,struct sockaddr* peeraddr,socklen_t addrlen);

sockfd:操作句柄
data:要发送的数据的首地址
len:要发送的数据长度
flag:默认为0,阻塞发送
peeraddr:对端地址信息
addrlen:地址结构长度
返回值:成功,返回实际发送的数据长度;失败,返回-1。

5、关闭套接字:int close(int fd);

fd:操作句柄

流程外的重要接口:

字节序转换接口:

unint32_t htonl(uint32_t hostlong);----32位数据主机字节序到网络字节序的转换
unint16_t htons(uint32_t hostshort);----16位数据主机字节序到网络字节序的转换
unint32_t ntohl(uint32_t netlong);----32位数据网络字节序到主机字节序的转换
unint16_t ntohs(uint32_t netshort);----16位数据网络字节序到主机字节序的转换
注意:port端口转换使用htons/ntohs,ip转换使用htonl/ntohl,不能混用。

将字符串点分十进制IP地址转换为整型网络字节序IP地址:

“192.168.2.2”————》0xc0a80202
in_addr_t inet_addr(const char* cp);

将网络字节序IP地址转换为字符串点分十进制IP地址:

0xc0a80202————》“192.168.2.2”
char* inet_ntoa(struct in_addr in);

以上接口仅限于IPV4地址使用

不限于IPV4的地址转换:

int inet_pton(int af,const char* src,void* dst);
const char* inet_ntop(int af,void* src,char* dst,socklen_t size);
(1)这两个函数的af参数既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。如果,以不被支持的地址族作为af参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT.
(2)第一个函数尝试转换由src指针所指向的字符串,并通过dst指针存放二进制结果,若成功则返回值为1,否则如果所指定的af而言输入字符串不是有效的表达式格式,那么返回值为0.
(3)inet_ntop进行相反的转换,从数值格式(src)转换到表达式(dst)。inet_ntop函数的src参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。size参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。如果size太小,不足以容纳表达式结果,那么返回一个空指针,并置为errno为ENOSPC。

服务端代码

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>//字节序转换接口头文件
#include<netinet/in.h>//地址结构/协议类型头文件
#include<sys/socket.h>//套接字接口文件int main()
{//1.创建套接字//int socket(地址域类型,套接字类型,协议类型);int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(sockfd<0){perror("socket error:");return -1;}//2.为套接字绑定地址信息//int bind(操作句柄,地址结构信息,地址长度);struct sockaddr_in addr;//定义ipv4地址结构addr.sin_family=AF_INET;addr.sin_port=htons(9000);//设置端口addr.sin_addr.s_addr=inet_addr("192.168.85.128");int len=sizeof(addr);int ret=bind(sockfd,(struct sockaddr*)&addr,len);if(ret<0){perror("bind error:");return -1;}while(1){  //3.接收数据//recvfrom(句柄,空间,长度,标志,对端地址,地址长度)char buf[1024]={0};struct sockaddr_in paddr;int len=sizeof(struct sockaddr_in);ret= recvfrom(sockfd,buf,1023,0,(struct sockaddr*)&paddr,&len);if(ret<0){perror("recv error:");return -1;}uint16_t cport=ntohs(paddr.sin_port);char* cip=inet_ntoa(paddr.sin_addr);printf("client-[%s:%d]client say:%s\n",cip,cport,buf);//4.回复数据memset(buf,0x00,1024);printf("server say:");fflush(stdout);fgets(buf,1023,stdin);ret=sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&paddr,len);if(ret<0){perror("send error:");return -1;}}//5.关闭套接字close(sockfd);return 0;
}

客户端代码

头文件:

/** 封装一个udp_socket类* 通过实例化的对象调用对应的成员接口实现udp客户端及服务端搭建*/ #include<iostream>
#include<string>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<cstdio>
using namespace std;class UdpSocket{private:int _sockfd;public:UdpSocket():_sockfd(-1){}bool Socket(){_sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(_sockfd<0){perror("socket error:");return false;}return true;}bool Bind(std::string &ip,uint16_t port){struct sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(port);addr.sin_addr.s_addr=inet_addr(ip.c_str());socklen_t len=sizeof(struct sockaddr_in);int ret;ret=bind(_sockfd,(struct sockaddr*)&addr,len);if(ret<0){perror("bind error:");return false;}return true;}bool Send(std::string &data,const std::string &ip,int port){struct sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(port);addr.sin_addr.s_addr=inet_addr(ip.c_str());socklen_t len=sizeof(struct sockaddr_in);int ret=sendto(_sockfd,data.c_str(),data.size(),0,(struct sockaddr*)&addr,len);if(ret<0){perror("sendto error:");return false;}return true;}bool Recv(std::string *buf,std::string *ip=NULL,int  *port=NULL){struct sockaddr_in addr;socklen_t len=sizeof(struct sockaddr_in);char tmp[4096]={0};int ret=recvfrom(_sockfd,tmp,4096,0,(struct sockaddr*)&addr,&len);if(ret<0){perror("recvfrom error:");return false;}buf->assign(tmp,ret); //申请ret长度的空间,并且将tmp的数据拷贝过去if(ip!=NULL){*ip=inet_ntoa(addr.sin_addr);}if(port!=NULL){*port=ntohs(addr.sin_port);}return true;}bool Close(){if(_sockfd!=-1){close(_sockfd);}return true;}
};

主程序

#include"udp_socket.hpp"#define CHECK_RET(q) if((q)==false){return -1;}int main(){UdpSocket sock;//1.创建套接字CHECK_RET(sock.Socket());//2.绑定地址信息(客户端不推荐执行这一操作)while(1){//3.发送数据std::cout<<"client say:";std::string buf;std::cin>>buf;CHECK_RET(sock.Send(buf,"192.168.85.128",9000));//4.接收数据buf.clear();CHECK_RET(sock.Recv(&buf));std::cout<<"server say:"<<buf<<endl;}//5.关闭套接字sock.Close();return 0;
}

输出结果:

TCP通信程序

通信流程:

创建连接的 原理:

  1. 服务端创建一个套接字 S1(包含sip+sport+tcp) ,并将其置于listen状态,开始处理客户端的请求(S1仅用于接收新的客户端连接);
  2. 客户端向服务端发送一个连接请求,服务端通过复制 S1 为该客户端创建一个新的套接字(包含sip+sport+dip+dport+tcp);

接口:

1、创建套接字:int socket(int domain,int type,int protocol);

2、绑定地址信息:int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);

3、开始监听:int listen(int sockfd,int backlog);

sockfd:套接字描述符
backlog:服务端在同一时间能够处理的最大连接数
内核中有一个已完成连接队列----存储已经完成的客户端连接,如果这个队列已满,操作系统将不能再接收新的连接请求。
已完成连接队列的大小=backlog+1;

syn泛洪攻击:向服务端大量发送连接请求,阻碍服务端正常运转;解决办法:已完成连接队列,防火墙

4、客户端发送连接请求:int connect(int sockfd,struct sockaddr*srvaddr,socklen_t len);

sockfd: 套接字描述符
srvaddr: 服务端地址信息
len: 地址长度
返回值:成功返回0;失败返回-1。

5、服务端获取新建连接;int accept(int s, struct sockaddr *cliaddr, socklen_t *addrlen);

sockfd:监听套接字----服务端最早创建的,只用于获取新链接的套接字
cliaddr: 新的连接的客户端地址信息
addrlen:输入输出参数,指定地址信息长度,以及返回实际长度
返回值:新建连接的描述符----往后与客户端的通信都通过这个描述符完成。

6、收发数据:tcp通信因为socket中含有完整的五元组,所以收发数据数据都不需要指定地址
ssize_t send(int sockfd, void *data, int len, int flag);

sockfd: 描述符
data:要发送的数据
len:数据长度
flag:0----默认阻塞
返回值:成功返回实际发送的长度;失败返回-1;连接断开会触发异常

ssize_t recv(inst sockfd,void *buf,int len,int flag);

sockfd:描述符
buf:存放接收到的数据的空间地址
flag:0-----阻塞接收
返回值:成功返回实际收到的数据长度;出错返回-1;连接断开返回0.

7、关闭套接字:int close(int sockfd);

代码实例:

封装的socket类:


```cpp
#include<cstdio>
#include<unistd.h>
#include<iostream>
#include<string>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>#define CHECK_RET(q) if((q)==false){return -1;}
#define LISTEN_BACKLOG 5class TcpSocket{private:int _sockfd;public:TcpSocket():_sockfd(-1){}bool Socket(){_sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(_sockfd<0){perror("socket error:");return false;}return true;}bool Bind(const std::string &ip,const uint16_t port){struct sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(port);addr.sin_addr.s_addr=inet_addr(&ip[0]);socklen_t len=sizeof(sockaddr);int ret=bind(_sockfd,(sockaddr*)&addr,len);if(ret<0){perror("bind error:");return false;}return true;}bool Listen(int backlog = LISTEN_BACKLOG){//listen(描述符,同一时间最大连接数);int ret=listen(_sockfd,backlog);if(ret<0){perror("listen error");return false;}return true;}bool Connect(const std::string &ip,const uint16_t port){struct sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(port);addr.sin_addr.s_addr=inet_addr(&ip[0]);socklen_t len=sizeof(sockaddr);int ret=connect(_sockfd,(sockaddr*)&addr,len);if(ret<0){perror("connect error");return false;}return true;}bool Accept(TcpSocket* sock,std::string *ip=NULL,uint16_t *port=NULL){//int accept(监听套接字,获取客户端地址,长度)sockaddr_in addr;socklen_t len=sizeof(sockaddr_in);int newfd=accept(_sockfd,(sockaddr*)&addr,&len);if(newfd<0){perror("accept error");return false;}sock->_sockfd=newfd;if(ip!=NULL){*ip=inet_ntoa(addr.sin_addr);}if(port!=NULL){*port=ntohs(addr.sin_port);}return true;}bool Recv(std::string *buf){//int recv(描述符,空间,数据长度,标志位)//返回值:实际h获取的大小   0---连接断开;-1------出错了char tmp[4096]={0};int ret=recv(_sockfd,tmp,4096,0);if(ret<0){perror("recv error");return false;}else if(ret==0){printf("连接断开!\n");return false;}buf->assign(tmp,ret);return true;}bool Send(const std::string &data){//int send(描述符,数据,长度,标志位);int total=0;while(total<data.size()){int ret=send(_sockfd,&data[0]+total,data.size()-total,0);if(ret<0){perror("send error");return false;}total+=ret;}return true;}bool Close(){if(_sockfd!=-1){close(_sockfd);}return true;}
};

服务端代码

#include "tcpsocket.hpp"int main(int argc,char* argv[]){//命令行: ./tcp_srv 192.168.2.2   9000if(argc!=3){printf("格式:./tcp_src 192.168.2.2 9000\n");return -1;}std::string srvip=argv[1];uint16_t srvport=std::stoi(argv[2]);TcpSocket lst_sock;//监听套接字,仅用于接受新连接//1.创建套接字CHECK_RET(lst_sock.Socket());//2.绑定地址信息CHECK_RET(lst_sock.Bind(srvip,srvport));//3.开始监听CHECK_RET(lst_sock.Listen());while(1){//4.获取新连接TcpSocket cli_sock;//客户端套接字std::string cli_ip;//客户端ip地址uint16_t cli_port; //客户端端口bool ret=lst_sock.Accept(&cli_sock,&cli_ip,&cli_port);if(ret==false){//客户端发生错误,服务端不能退出,继续处理下一个客户端的请求continue;}std::cout<<"获取新建连接:"<<cli_ip<<":"<<cli_port<<std::endl;//5.收发数据---使用获取的新建套接字进行通信std::string buf;ret=cli_sock.Recv(&buf);if(ret==false){cli_sock.Close();continue;}std::cout<<"client say:"<<buf<<std::endl;buf.clear();std::cout<<"server say:";std::cin>>buf;ret=cli_sock.Send(buf);if(ret==false){cli_sock.Close();}}//6.关闭套接字lst_sock.Close();return 0;
}

客户端代码

#include "tcpsocket.hpp"int main(int argc, char *argv[])
{//通过参数传入要连接的服务端的地址信息if (argc != 3) {printf("输入格式: ./tcp_cli srvip srvport\n");return -1;}std::string srvip = argv[1];uint16_t srvport = std::stoi(argv[2]);TcpSocket cli_sock;//1. 创建套接字CHECK_RET(cli_sock.Socket());//2. 绑定地址信息(不推荐)//3. 向服务端发起连接CHECK_RET(cli_sock.Connect(srvip, srvport));while(1){   //4. 收发数据std::string buf;std::cout<<"client say:";std::cin>>buf;CHECK_RET(cli_sock.Send(buf));   //发送数据buf.clear();CHECK_RET(cli_sock.Recv(&buf));   //接收数据std::cout<<"server say:"<<buf<<std::endl;}//5. 关闭套接字CHECK_RET(cli_sock.Close());return 0;
}

出现问题:

上述代码虽能正常运行,但同时也存在很大的问题:
accept、recv和send都是阻塞接口,任意一个接口的调用都有可能会导致服务端流程阻塞

本质原因:当前的服务端不知道什么时候又新连接到来,什么时候哪个客户端有数据到来,因此流程只能固定的去调用接口,但是这种调用方式可能会造成阻塞

解决方案:

多执行流并发处理----为每个客户端创建一个执行流负责这个客户端的通信
好处:

  1. 即使主线程被卡在获取新连接这一步,也不会影响其他执行流中客户端的通信
  2. 某个客户端阻塞,不会影响主线程和其他线程

具体操作:在主线程中获取新建连接,一旦获取到了就创建一个执行流,通过这个新建连接与客户端进行通信

多线程:普通线程与主线程数据共享,指定入口函数执行;主线程不能随意释放套接字,因为资源共享,一旦释放,其他线程无法使用

多进程:子进程复制了父进程,但是数据独有;要注意僵尸进程的处理,注意父子进程数据独有,父进程用不到套接字资源因此创建子进程之后要记得释放掉,否则会造成资源泄露。

优化后的代码

多线程:服务端

#include "tcpsocket.hpp"
#include <pthread.h>void *thr_entry(void *arg)
{bool ret;TcpSocket *clisock = (TcpSocket*)arg;while(1) {//5. 收发数据--使用获取的新建套接字进行通信std::string buf;ret = clisock->Recv(&buf);if (ret == false) {clisock->Close();delete clisock;return NULL;}std::cout << "client say: " << buf << std::endl;buf.clear();std::cout << "server say: ";std::cin >> buf;ret = clisock->Send(buf);if (ret == false) {clisock->Close();delete clisock;return NULL;}}clisock->Close();delete clisock;return NULL;
}
int main(int argc, char *argv[])
{//通过程序运行参数指定服务端要绑定的地址// ./tcp_srv 192.168.2.2 9000if (argc != 3) {printf("usage: ./tcp_src 192.168.2.2 9000\n");return -1;}std::string srvip = argv[1];uint16_t srvport = std::stoi(argv[2]);TcpSocket lst_sock;//监听套接字//1. 创建套接字CHECK_RET(lst_sock.Socket());//2. 绑定地址信息CHECK_RET(lst_sock.Bind(srvip, srvport));//3. 开始监听CHECK_RET(lst_sock.Listen());while(1) {//4. 获取新建连接TcpSocket *clisock = new TcpSocket();std::string cliip;uint16_t cliport;bool ret = lst_sock.Accept(clisock, &cliip,&cliport);if (ret == false) {continue;}std::cout<<"get newconn:"<< cliip<<"-"<<cliport<<"\n";//创建线程专门负责与指定客户端的通信pthread_t tid;pthread_create(&tid, NULL, thr_entry, (void*)clisock);pthread_detach(tid);}//6. 关闭套接字lst_sock.Close();return 0;
}

多进程:服务端

#include "tcpsocket.hpp"
#include <signal.h>
#include <sys/wait.h>void sigcb(int no)
{while(waitpid(-1, NULL, WNOHANG) > 0);
}void worker(TcpSocket &clisock)
{  //child processbool ret;while(1) {//5. 收发数据--使用获取的新建套接字进行通信std::string buf;ret = clisock.Recv(&buf);if (ret == false) {clisock.Close();exit(0);}std::cout <<"client say: "<<buf<<std::endl;buf.clear();std::cout << "server say: ";std::cin >> buf;ret = clisock.Send(buf);if (ret == false) {clisock.Close();exit(0);}}clisock.Close();//释放的是子进程的clisockexit(0);return;
}
int main(int argc, char *argv[])
{//通过程序运行参数指定服务端要绑定的地址// ./tcp_srv 192.168.2.2 9000if (argc != 3) {printf("usage: ./tcp_src 192.168.2.2 9000\n");return -1;}signal(SIGCHLD, SIG_IGN);//signal(SIGCHLD, sigcb);std::string srvip = argv[1];uint16_t srvport = std::stoi(argv[2]);TcpSocket lst_sock;//监听套接字//1. 创建套接字CHECK_RET(lst_sock.Socket());//2. 绑定地址信息CHECK_RET(lst_sock.Bind(srvip, srvport));//3. 开始监听CHECK_RET(lst_sock.Listen());while(1) {//4. 获取新建连接TcpSocket clisock;std::string cliip;uint16_t cliport;bool ret = lst_sock.Accept(&clisock, &cliip,&cliport);if (ret == false) {continue;}std::cout<<"get newconn:"<< cliip<<"-"<<cliport<<"\n";pid_t pid = fork();if (pid < 0) {clisock.Close();continue;}else if (pid == 0) {worker(clisock);}//父子进程数据独有,父进程关闭不会对子进程造成影响clisock.Close();//释放的是父进程中的clisock}//6. 关闭套接字lst_sock.Close();return 0;
}

网络基础:套接字编程,UDP和TCP通信程序相关推荐

  1. 【网络】网络基础套接字编程详解

      目录 网络初识 1.网络协议初识 2.网络协议的分层: OSI分层模型--->网络理论模型: TCP/IOP五层模型--->工业中采用的网络模型 为什么要有网络分层? 3.网络数据的封 ...

  2. 计算机网络(二) | 网络编程基础、Socket套接字、UDP和TCP套接字编程

    目录 一.网络编程基础 1.1 为什么需要网络编程 1.2 什么是网络编程 1.3 网络编程中的基本概念 二.Socket套接字 2.1 概念 2.2 分类 2.3 Java数据报套接字通信模型 2. ...

  3. 套接字编程---2(TCP套接字编程的流程,TCP套接字编程中的接口函数,TCP套接字的实现,TCP套接字出现的问题,TCP套接字多进程版本,TCP套接字多线程版本)

    TCP模型创建流程图 TCP套接字编程中的接口 socket 函数 #include <sys/types.h> /* See NOTES */ #include <sys/sock ...

  4. 网络开发套接字以及UDP、TCP协议

    目录 一.Socket 套接字 1.Socket 套接字的概念 2.Socket 套接字分类 3.Java数据报套接字通信模型 4.Java流套接字通信模型 5.Socket编程注意事项 二.UDP数 ...

  5. 糖儿飞教你学C++ Socket网络编程——5.2 TCP通信程序的函数及流程总结

    TCP服务器端程序流程 监听套接字=socket(AF_INET, 套接字的类型, 0) bind(监听套接字, 本地地址, 地址长度) 通信套接字=accept(监听套接字, 对方地址, 地址长度的 ...

  6. [Linux](16)网络编程:网络概述,网络基本原理,套接字,UDP,TCP,并发服务器编程,守护(精灵)进程

    文章目录 网络协议初识 OSI 七层模型 TCP/IP 四层(或五层)模型 IP.MAC.端口号 TCP 协议与 UDP 协议 套接字 套接字地址结构 socket 函数 bind 函数 recvfr ...

  7. 《Unix网络编程》卷一(简介TCP/IP、基础套接字编程)

    通常说函数返回某个错误值,实际上是函数返回值为-1,而全局变量errno被置为指定的常值(即称函数返回这个错误值). exit终止进程,Unix在一个进程终止时总是关闭该进程所有打开的描述符. TCP ...

  8. 网络基础2【HTTP、UDP、TCP】

    目录 一.应用层 1.协议 2.网络版计算器 3.HTTP协议 (1)了解url和http (2)http的用处 (3)urlencode和urldecode (4)http协议格式 4.HTTPS协 ...

  9. JAVA网络编程→BIO到NIO、IP域名服务器DNS端口、套接字编程Socket、TCP与UDP

    视之不见名曰夷:听之不闻名曰希:搏之不得名曰微. 此三者不可致诘,故混而为一. 其上不皦,其下不昧,绳绳兮不可名,复归于无物. 是谓无状之状,无物之象,是谓惚恍. 迎之不见其首:随之不见其后. 执古之 ...

最新文章

  1. 【camera-lidar-radar】基于卡尔曼滤波和扩展卡尔曼滤波的相机、激光雷达、毫米波雷达多传感器后融合
  2. 常见的计算机视觉任务综述
  3. 关于Mysql5.6半同步主从复制的开启方法【转】
  4. reduce_sum() got an unexpected keyword argument 'keep_dims'
  5. Centos7安装Python3并更改默认版本为python3(编译安装)
  6. 流计算程序不报错,但是没有输出的一些原因(持续更新中)
  7. as400还有发展前景吗_web前端还有发展前景吗?该如何去学习
  8. ELK和EFK的区别
  9. 14、查找最接近的元素
  10. oracle字段枚举值,E.1.9 结构、联合、枚举和位字段 (G.3.9)
  11. CoreData的数据迁移
  12. 原 Spring RestTemplate中几种常见的请求方式
  13. 在visio里面插入带圆圈的数字字符
  14. win7下虚拟显示器完成记(virtual monitor)——VDI显卡透传场景
  15. JAVA中 万物皆对象
  16. Antd design pro 网站favicon.ICO图标 网页LOGO图标 左侧边栏LOGO文字 默认登陆页面LOGO 页面Loading样式修改
  17. CampusBulider(模模搭)学习笔记6:室内搭建
  18. Html学习(二)font 加粗 斜体 下划线标签学习
  19. TYVJ p1035 棋盘覆盖
  20. 手机号 电话号码 邮箱 验证

热门文章

  1. 使用python turtle库绘制一个三角形_使用turtle库绘制一个叠加等边三角形,图形效果如下:...
  2. 2021年必备的最全人工智能书单
  3. 如何用navicat导入数据?
  4. flutter Container 的decoration 属性
  5. Win7系统修复启动项命令
  6. 解决方案:IDEA没有Java Enterprise选项的问题
  7. 软件测试建模:Google ACC
  8. SO逆向入门实战教程九——blackbox
  9. matlab 将矩阵转化成向量的两个方法
  10. skycc在线seo外链工具 V9.3免费版