【Linux】网络基础(二)

2023-10-26

UDP 协议通信程序的编写

UDP 协议:无连接、不可靠、基于数据报传输

两端通信流程:
在这里插入图片描述

套接字相关接口介绍

#include <sys/socket.h> 头文件

一、 创建套接字
int socket(int domain, int type, int protocol);
(1) domain:地址域类型(域间通信、ipv4通信、ipv6通信… 不同通信有不同的地址结构)在这里插入图片描述

(2) type:套接字类型
SOCK_STREAM:流式套接字,提供字节流传输,默认协议是 TCP 协议
SOCK_DGRAM:数据报套接字,提供的是数据报传输,默认协议是 UDP 协议在这里插入图片描述

(3) protocol:协议类型,默认使用 0,表示使用套接字类型对应的默认协议
IPPROTO_TCP:值为 6
IPPROTO_UDP:值为 17

返回值:成功返回一个套接字描述符,失败返回 -1

二、为套接字绑定地址信息
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
(1) sockfd:socket 返回的套接字描述符
(2) my_addr:要绑定的地址信息
但是不同地址域类型具有不同的地址结构在这里插入图片描述

ipv4 地址域类型 2B;端口号 2B;IP地址 4B
ipv6 地址域类型 2B;端口号 4B;IP地址 4B

(3) addrlen:绑定地址信息的长度
返回值:成功返回 0 ,失败返回 -1

三、发送数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
(1) sockfd:socket 返回的套接字描述符
(2) buf:要发送的数据空间起始地址
(3) len:要发送的数据长度,从 buf 地址开始,发送 len 长度的数据
(4) flags:默认 0-阻塞发送(发送缓冲区数据满了则等待)
(5) dest_addr:对端地址信息,描述数据要发送给谁(设置地址信息)
(6) addrlen:对端地址信息长度
返回值:成功返回实际发送数据字节长度,失败返回 -1

四、接收数据
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
(1) sockfd:socket 返回的套接字描述符
(2) buf:空间起始位置,接收到的数据存放在 buf 空间中
(3) len:想要接收到的数据长度
(4) flags:默认 0-阻塞接收(socket 接收缓冲区中没有数据则阻塞,直到有数据)
(5) src_addr:接收数据的同时,需要知道数据是谁发的,(获取地址信息)输出参数,用于返回对端地址信息
(6) addrlen:地址信息长度,输入输出参数,表示想要接收的地址信息长度以及实际得到的地址信息长度
返回值:成功返回实际接收到的数据长度,失败返回 -1

五、关闭套接字,释放资源
int close(int fd);

字节序相关接口

注意 32 位数据转换接口与 16 位数据转换接口不可混用

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
主机字节序到网络字节序的转换

uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
网络字节序到主机字节序的转换

示例说明:

在这里插入图片描述

IP 地址转换接口

in_addr_t inet_addr(const char *cp); 将点分十进制IP地址转换为网络字节序整型IP地址

const char *inet_ntoa(struct in_addr in); 将网络字节序整型IP地址转换为点分十进制IP地址
inet_ntoa 接口,若返回的是一个局部变量(函数退出就会释放),这个函数返回的是静态变量地址

在这里插入图片描述
在这里插入图片描述

测试代码:

服务端:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<unistd.h>
  5 #include <arpa/inet.h>  //字节序转换接口头文件
  6 #include <sys/socket.h>  //套接字接口头文件
  7 #include <netinet/in.h>  //地址结构类型以及协议类型宏头文件
  8 
  9 int main(int argc,char* argv[])
 10 {
 11   if(argc!=3){
 12     printf("./udp_ser 192.168.2.2 9000\n");
 13     return -1;
 14   }
 15   uint16_t port=atoi(argv[2]);   //端口信息,uint16 属于 ipv4 数据类型----包含有2字节16位数据 ,atoi 将字符串转换为整数
 16   char* ip=argv[1] ;  //ip 地址信息
 17 
 18   //1、创建套接字
 19   //int socket(int domain, int type, int protocol);
 20   int sockfd=socket(AF_INET ,SOCK_DGRAM,IPPROTO_UDP);                           
 21                //AF_INET 代表的是 ipv4 地址域类型,SOCK_DGRAM 表示数据报套接字-UDP,IPPROTO_UDP 表示 UDP 协议类型
 22 
 23   if(sockfd<0){
 24     perror("socket error!\n");
 25     return -1;
 26   }
 27 
 28   
 29   //2、为套接字绑定地址信息
 30   //int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);          
 31     
 32   struct sockaddr_in addr; //定义一个 IPV4 地址结构
 33   addr.sin_family=AF_INET;  //定义为 IPV4 地址域类型
 34   addr.sin_port=htons(port);  //ipv4 地址域端口为 uint16_t 对应 16 位----使用 htons 进行主机字节序端口到网络字节序端口的转换
 35   addr.sin_addr.s_addr=inet_addr(ip);  //将点分十进制字符串 IP 地址转换为整型IP地址
 36   socklen_t len=sizeof(struct sockaddr_in);   //计算地址信息长度
 37 
 38   int ret=bind(sockfd,(struct sockaddr*)&addr,len);    //传递地址信息需要进行类型强转
 39   if(ret<0){
 40     perror("bind error!\n");  //套接字绑定地址信息失败
 41     return -1;
 42   }
 43 
 44   //3、循环接收发送数据
 45   while(1){
 46     //接收信息
 47     //ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
 48     char buff[1024]={0};  //接收到的信息存放地址
 49     struct sockaddr_in peer;  //从哪里接收到的信息
 50     socklen_t len=sizeof(struct sockaddr_in);  //接受的数据长度
 51     
 52     ssize_t ret= recvfrom(sockfd,buff,1023,0,(struct sockaddr*)&peer,&len);  //0-阻塞
 53     if(ret<0){
 54       perror("recvfrom error!\n");                                              
 55       return -1;  
 56     }
 57     
 58 
 59     char* peerip=inet_ntoa(peer.sin_addr);  //数据发送到哪里,将整形 IP 地址转换为点分十进制的 IP 地址
 60     uint16_t peerport=ntohs(peer.sin_port);   //ipv4 地址域类型为 uint16_t 含有16 位数字-->ntohs,将网络字节端口转换为主机字节序端口
 61 
 62     printf("client[%s : %d] say:%s",peerip,peerport,buff);
 63     
 64 
 65     //发送信息
 66     //ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
 67     char data[1024]={0} ;  //要发送的数据存放位置
 68     printf("server say: ");                                                     
 69     fflush(stdout);
 70     scanf("%s",data);
 71     
 72     ret=sendto(sockfd,data,strlen(data),0,(struct sockaddr*)&peer,len);  //0-阻塞
 73     if(ret<0){
 74       perror("sendto error!\n");
 75       return -1;
 76     }
 77 
 78   }
 79   //关闭套接字
 80   close(sockfd);
 81 
 82   return 0;
 83 }

进行绑定(bind)端口时候:
虚拟机绑定的是 ens33 处的网卡信息

在这里插入图片描述
云服务器绑定的是 etho 处的网卡信息

端口是一个 uint16_t 类型的数据,范围 0~65535
但是 0~1023 这些端口被一些知名服务使用,例如 ssh–22号,http–80号端口
因此在使用时候尽量使用 1024 以上的端口,防止端口冲突导致绑定失败。

对于服务端或客户端都会存在:
创建套接字、绑定地址(客户端不需要绑定)、发送数据、接收数据、关闭套接字,因此我们可以将套接字的一系列接口进行封装来使用:

  1 #include <iostream>
  2 #include<assert.h>
  3 #include<unistd.h>
  4 #include<string>
  5 #include <cassert>
  6 #include<sys/socket.h>
  7 #include<arpa/inet.h>  //字节序转换接口头文件
  8 #include<netinet/in.h>  //地址结构类型以及协议类型宏头文件
  9 
 10 //进行 UDP 接口的封装
 11 
 12 class UdpSocket{
 13   private:
 14     int _sockfd;
 15   public:
 16     UdpSocket():_sockfd(-1) {}
 17     ~UdpSocket() {
 18       Close();
 19     }
 20 
 21     //创建套接字                                                                   
 22     bool Socket(){
 23       _sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);//定义为 IPV4 地址域,数据报传输(UDP),定义传输协议为 UDP 协议
 24       if(_sockfd<0){
 25         perror("socket error!\n");
 26         return false;  
 27       }
 28       return true;  //创建套接字成功
 29     }
 30                                                                                    
 31     //给套接字绑定地址空间
 32     bool Bind(const std::string& ip,uint16_t port)
 33     {
 34       //创建 ipv4 地址域结构 struct sockaddr_in 类型
 35       struct sockaddr_in addr;
 36       addr.sin_family=AF_INET;  //ipv4 地址域类型
 37       addr.sin_port=htons(port);  //将主机字节序转换为网络字节序---16位使用 htons       
 38       addr.sin_addr.s_addr=inet_addr(ip.c_str());  //将 IP 地址转换为整型 IP 
 39       socklen_t len=sizeof(struct sockaddr_in);
 40 
 41       int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
 42       if(ret<0){
 43         perror("bind error!\n");
 44         return false;
 45       }
 46       return true;
 47     }
 48 
 49     //接收数据
 50     bool Recv(std::string& body,std::string* peer_ip = NULL,uint16_t* peer_port=NULL)
 51     {
 52       struct sockaddr_in peer;
 53       socklen_t len=sizeof(struct sockaddr_in);
 54       char tmp[4096]={0};
 55       ssize_t ret=recvfrom(_sockfd,tmp,4096,0,(struct sockaddr*)&peer,&len);   //0-阻塞接收
 56       if(ret<0){
 57         perror("recvfrom error!\n");                                               
 58         return false;
 59       }
 60       if(peer_ip!=NULL)
 61         *peer_ip=inet_ntoa(peer.sin_addr);  //获取到源端IP信息,转换为点分十进制IP地址
 62       if(peer_port!=NULL)
 63         *peer_port=ntohs(peer.sin_port);  //将网络套接字转换为主机套接字
 64       body.assign(tmp,len);  //从 tmp 获取 len 长度字节放入到 body 中
 65       return true;
 66     }
 67 
 68 
 69     //发送数据
 70     bool Send(const std::string& body,const std::string& peer_ip,uint16_t peer_port    )
 71     {
 72       struct sockaddr_in addr;
 73       addr.sin_family=AF_INET;
 74       addr.sin_port=htons(peer_port);  //将点云十进制 IP 转换为整型 IP
 75       addr.sin_addr.s_addr=inet_addr(peer_ip.c_str());  //将主机字节序转换为网络字节序
 76       socklen_t len=sizeof(struct sockaddr_in);
 77                                                                                    
 78       ssize_t ret=sendto(_sockfd,body.c_str(),body.size(),0,(struct sockaddr*)&addr,len);
 79       if(ret<0){
 80         perror("sendto error!\n");
 81         return false;
 82       }
 83       return true;
 84     }
 85 
 86     //关闭套接字
 87     bool Close()
 88     {
 89       if(_sockfd!=-1){
 90         close(_sockfd);
 91         _sockfd=-1;
 92       }
 93       return true;
 94     }
 95 };

客户端-服务端代码可以参考:
添加链接描述
添加链接描述

运行结果显示:
在这里插入图片描述

网络通信程序进行通信时候有可能是跨主机的,因此会涉及到防火墙设置:

(虚拟机)需要先停用防火墙才能进行通信
1、sudo systemctl stop firewalld 停止防火墙服务
2、sudo systemctl diable firewalld 禁用防火墙(下次开始依然不会重启)
虚拟机只能实现与自己电脑、或虚拟机内部通信,无法实现跨主机(因为虚拟机IP是私有地址且对外隐藏)
NAT 地址转换技术----将私网内的主机对外发送的数据的源端地址进行替换为对外地址

(云服务器)登录云服务器网站,进行安全策略组设置,开通指定要访问的接口,才能实现对外访问

TCP 协议通信程序的编写

TCP :面向连接、可靠传输、面向字节流的传输

因为 TCP 是面向连接的,在进行通信之前必须先建立连接(一对一),因此在 TCP 通信中不限定必须是客户端先进行发送数据

TCP 通信程序中:
客户端向服务器发送一个连接建立请求,服务端处于监听状态,则会对这个连接建立请求进行处理----> (1)为这个新连接请求,创建一个套接字结构体;(2)为这个新的套接字,描述完整的五元组信息;往后的数据通信就由这个套接字进行通信

一个服务器上有多少客户端想要建立连接,服务端就要创建多少个套接字(源端IP及端口都是一样的);而最早的服务端创建监听套接字则只负责新连接请求处理,不负责数据的通信

服务端会为每个客户端都创建一个新的套接字,负责与这个客户端进行数据通信,但是想要通过这个套接字与客户端进行通信就必须知道这个套接字的描述符

通信流程:

在这里插入图片描述

在这里插入图片描述

套接字接口介绍

一、创建套接字
int socket(int domain, int type, int protocol);
(1)domain:地址域类型-----ipv4 通信 AF_INET
(2)type:套接字类型–字节流传输 – SOCK_STREAM
(3)protocol:协议类型: IPPROTO_TCP

二、建立连接
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
(1)sockfd:socket 返回的套接字描述符
(2)my_addr:要绑定的地址信息
(3)addrlen:要绑定的地址信息长度
返回值:成功返回 0,失败返回 -1

三、开始监听
int listen(int socket, int backlog);
(1)socket:创建套接字返回的监听套接字描述符
(2)backlog:同一时刻最大并发连接数(限制同一时间有多少个客户端连接请求能够被处理-----否则会导致系统崩溃(连续创建多个套接字导致系统资源耗尽))
返回值:成功返回 0 ,失败返回 -1

-内核中每个监听套接字都有一个对应新连接的socket队列:半连接队列 / 已完成连接队列 (半连接队列被放满,当在有新的连接需要建立时会被直接丢弃)

-listen 函数第二个参数 backlog 限制的就是队列中节点数量 = backlog + 1

泛洪攻击------SYN cookies (自行调研)

四、向服务端发送连接建立请求(客户端使用)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
(1)sockfd:套接字描述符
(2)addr:服务端地址信息
(3)addrlen:地址长度

五、获取新建连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
从内核 sockfd 指定的监听套接字对应的已完成连接队列中,取出一个 socket 并返回其描述符,通过 addr 参数返回值可以知道具体连接请求来源于哪个客户端
(1)sockfd:套接字描述符,是我们创建的套接字 sockfd
(2)addr:accept 接收到的填充客户端地址----输出参数
(3)addrlen:地址信息长度–输入输出参数,用于指定想要获取的地址长度以及返回实际地址信息长度
返回值:成功返回新连接的套接字描述符,出错返回 -1

六、收发数据
int send(int sockfd, const void *msg, size_t len, int flags);
sockfd 是 accept 函数中返回的新建连接的 sockfd
相较于 sendto 不需要指定 IP 和端口信息
返回值:成功返回实际发送数据的长度,失败返回 -1

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd 是 accept 函数中返回的新建连接的 sockfd
相较于 recvfrom 不需要获取 IP 和端口信息
返回值:成功返回实际发送的字节长度,出错返回 -1 ,连接断开返回 0

recv 返回值为 0 ,主要是告诉程序员连接断开了

TCP 是面向连接的,一旦连接断开将无法实现通信(对方关闭了连接/网络出错)

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

在这里插入图片描述

测试代码:

对 TCP 相关接口进行封装

  1 #include<iostream>
  2 #include<string>
  3 #include<stdio.h>                                                      
  4 #include<unistd.h>
  5 #include <sys/socket.h>
  6 #include <arpa/inet.h>
  7 #include <netinet/in.h>
  8 
  9 #define BACKLOG 1024    //宏定义:同一时刻最大客户端建立连接数
 10 
 11 //封装一个 TCP 接口
 12 class TcpSocket{
 13   private:
 14     int _sockfd;
 15   public:
 16     TcpSocket():_sockfd(-1) {}
 17     ~TcpSocket(){
 18         Close();
 19         _sockfd=-1;
 20     }
 21 
 22     //创建套接字:TCP 协议
 23     bool Socket()
 24     {    //SOCK_STREAM :字节流传输   ,IPPROTO_TCP :表示使用 TCP 协议
 25       _sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 26       if(_sockfd<0){
 27         perror("socket error!\n");
 28         return false;
 29       }
 30       return true;
 31     }
 32     //绑定端口
 33     bool Bind(const std::string &ip,uint16_t port)
 34     {  //建立 ipv4 地址结构
 35       struct sockaddr_in addr;  //ipv4 地址结构
 36       addr.sin_family=AF_INET;  //ipv4 地址域                          
 37       addr.sin_port=htons(port);   //将主机套接字转换为网络套接字
 38       addr.sin_addr.s_addr=inet_addr(ip.c_str());  //将地址信息转化为整型 IP 地址
 39       socklen_t len=sizeof(struct sockaddr_in);
 40 
 41       int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
 42       if(ret<0){
 43         perror("bind error!\n");
 44         return false;
 45       }
 46       return true;
 47     }
 48 
 49     //开始监听
 50     bool Listen(int backlog = BACKLOG)
 51     {
 52       int ret=listen(_sockfd,backlog);
 53       if(ret<0){
 54         perror("listen error!\n");                                     
 55         return false;
 56       }
 57       return true;
 58     }
 59 
 60     //建立连接:客户端申请向服务端进行连接(只要客户端需要该接口)
 61     bool Connect(const std::string& srvip,uint16_t srvport)
 62     {
 63       struct sockaddr_in addr;
 64       addr.sin_family=AF_INET;
 65       addr.sin_port=htons(srvport); //主机字节序转换为网络字节序
 66       addr.sin_addr.s_addr=inet_addr(srvip.c_str());  //将地址信息转化为整型 IP 地址
 67       socklen_t len=sizeof(struct sockaddr_in);
 68 
 69       int ret=connect(_sockfd,(struct sockaddr*)&addr,len);
 70       if(ret<0){
 71         perror("connect error!\n");
 72         return false;
 73       }
 74       return true;
 75     }
 76 
 77     //接收连接,获取新建连接的客户端地址信息
 78     bool Accept(TcpSocket* new_sock,std::string* cli_ip=NULL,uint16_t*cli_port=NULL)                                                         
 79     {
 80       struct sockaddr_in addr;
 81       socklen_t len=sizeof(struct sockaddr_in);
 82       int newfd=accept(_sockfd,(struct sockaddr*)&addr,&len);
 83       if(newfd<0){
 84         perror("accept error!\n");
 85         return false;
 86       }
 87       new_sock->_sockfd=newfd;       //新连接的套接字描述符
 88       if(cli_ip != NULL)
 89         *cli_ip=inet_ntoa(addr.sin_addr); //将获取到的客户端 IP 地址转换为整型
 90       if(cli_port!=NULL)
 91         *cli_port=ntohs(addr.sin_port);//将客户端端口从网络套接字 IP 转换为主机套接字(因为 ipv4 地址域类型为 uint16 含有两字节因此使用 ntohs    )
 92       return true;
 93     }
 94 
 95     //发送数据
 96     bool Send(const std::string& body)
 97     {
 98       ssize_t ret=send(_sockfd,body.c_str(),body.size(),0);  //0-阻塞等待,此处 _sockfd 为已经建立连接的套接字描述符
 99       if(ret<0){                                                       
100         perror("send error!\n");
101         return false;
102       }
103       return true;
104     }
105 
106 
107     //接收数据
108     bool Recv(std::string* body)
109     {
110       char tmp[1024]={0};
111       //recv 返回值大于 0;等于 0(表示断开连接);小于0(表示出错)
112       ssize_t ret =recv(_sockfd,tmp,1023,0);  //0-阻塞等待  ,此处 _sockfd 为已经建立连接的套接字描述符
113       if(ret<0){
114         perror("recv error!\n");
115         return false;
116       }
117       else if(ret==0){        //连接断开
118         printf("connect shutdown!\n");
119         return false;
120       }                                                                
121       body->assign(tmp,ret);  //从 tmp 截取 ret 长度数据放到 body 
122       return true;
123     }
124     
125     //关闭套接字
126     bool Close()
127     {
128       if(_sockfd!=-1){
129         close(_sockfd);
130         _sockfd=-1;
131       }
132       return true;
133     }
134 };

在客户端与服务端我们进行调用所封装的 TcpSocket 类来实现套接字创建、建立连接、接收连接、收发数据、关闭套接字信息等。

但是在代码运行过程中我们会发现一些问题:

问题一

在这里插入图片描述
在这里插入图片描述

服务端 TcpSocket new_sock 是一个局部变量,while 循环一次之后就会被释放析构,因此在服务端接收数据时候会发现 recv 返回值为 0 - 连接断开(connect shutdown 信息提示)

当我们将封装好的 TcpSocket 类内析构函数中将关闭套接字接口去掉之后,又发现一个新问题-------> 客户端只能与服务端建立一次通信:

在这里插入图片描述

在这里插入图片描述

这主要是因为 TcpSocket new_sock 是一个局部变量,每一次循环进入之后都会创建一个新套接字,导致只能接受新连接(新建立的客户端的通信)的一次通信,因此导致一个客户端无法与服务端建立连续的通信------------------那么,我们考虑能否将收发数据变为循环进行的,从而实现同一个客户端与服务端的连续通信

问题二

在服务端内部收发数据部分添加 while 循环时发现可以建立多次通信,但是多个客户端运行时只有一个客户端能够与服务器建立通信,而其他客户端不能进行与服务器的通信

在这里插入图片描述

在这里插入图片描述

而当我们关闭第一个客户端的连接之后,发现服务端卡死:

在这里插入图片描述

这主要是因为,我们在服务端循环收发数据时候使用的是 continue ,当接收/发送数据失败时候,跳出当前循环等待下一次接收/发送数据,从而导致了服务端卡死,因此我们需要将 continue 变为 break 跳出本次循环,结束当前的数据收发:

在这里插入图片描述

本质原因:

在这里插入图片描述

多执行流要进行多任务处理有两种方案:
多进程 & 多线程
多进程:安全、健壮
多线程:通信灵活、消耗小

多进程实现多执行流

我们采用多进程来解决以上问题:
主进程只负责一件事----->获取新连接,获取成功则创建一个新的子进程,让子进程来实现与客户端的通信

但是要考虑子进程退出形成僵尸进程,因此需要忽略子进程退出信号:

在这里插入图片描述

此时,多个客户端可以完成与服务端的正常通信:

在这里插入图片描述

注意

在创建子进程之后,要考虑的第一个问题就是僵尸进程—子进程退出,为了保存退出返回值而导致子进程资源没有完全释放,因此我们进行了一个信号的忽略处理 signal(SIGCHLD,SIG_IGN)

同时还需考虑一个问题:子进程创建 fork() 之后会导致父子进程代码共享数据独有,子进程来负责与客户端的通信,而父进程负责创建新建连接,但是在父进程这边,只需要创建新建连接获取新建连接的套接字描述符,在后期父进程并未使用该套接字描述符,因此会存在一个内存泄漏问题(由于之前我们去除掉了析构函数中的套接字关闭接口),因此在子进程进行收发数据失败之后只关闭了子进程中的套接字描述符,而父进程中没有关闭
所以,在父进程的运行中,我们需要调用套接字关闭接口来避免内存泄漏

在这里插入图片描述

客户端-服务端代码实现:
添加链接描述
添加链接描述

多线程实现多执行流

将服务端的数据收发处理交给不同的线程来进行处理,因此在完成一个新建连接之后,我们需要进行线程的创建,成功创建则进行数据收发的处理

服务端代码:
(在这段代码中我们采用的是类型强制转换,因此运行时会有一个警告)
添加链接描述

改进:
使用指针类型参数实现:

添加链接描述

在这里插入图片描述

在这里插入图片描述

在外面运行程序时,常会有一个提示说绑定地址失败:
在这里插入图片描述

主要是因为当前这个端口被占用

netstat -anptu:

查看当前主机上所有网络连接状态
a:all 所有连接信息
n : 以 IP 地址和端口号显示,而不要用服务名称来显示—> 127.0.0.1 – localhost ,22 – ssh 端口
p:显示连接对应的进程 ID 和 名称
t:tcp 套接字信息
u:udp 套接字信息

在这里插入图片描述

欢迎点赞留言鸭!!

在这里插入图片描述

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

【Linux】网络基础(二) 的相关文章

  • 强制卸载 NFS 安装目录 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案
  • 抑制 makefile 中命令调用的回显?

    我为一个作业编写了一个程序 该程序应该将其输出打印到标准输出 分配规范需要创建一个 Makefile 当调用它时make run gt outputFile应该运行该程序并将输出写入一个文件 该文件的 SHA1 指纹与规范中给出的指纹相同
  • 如何检测并找出程序是否陷入死锁?

    这是一道面试题 如何检测并确定程序是否陷入死锁 是否有一些工具可用于在 Linux Unix 系统上执行此操作 我的想法 如果程序没有任何进展并且其状态为运行 则为死锁 但是 其他原因也可能导致此问题 开源工具有valgrind halgr
  • 如何使用 bash 锁定文件

    我有一个任务从远程服务器同步目录 rsync av email protected cdn cgi l email protection srv data srv data 为了使其定期运行并避免脚本 reEnter 问题 我使用 rsyn
  • 如何使用 xterm.js 创建基于 Web 的终端以 ssh 进入本地网络上的系统

    我偶然发现了这个很棒的图书馆xterm js https xtermjs org 这也是 Visual Studio Code 终端的基础 我有一个非常普遍的问题 我想通过基于网络的终端 不在网络中 可能位于 aws 服务器上 访问本地网络
  • 仅打印“docker-container ls -la”输出中的“Names”列

    发出时docker container ls la命令 输出如下所示 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a67f0c2b1769 busybox tail f dev
  • 如何禁用 GNOME 桌面屏幕锁定? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何阻止 GNOME 桌面在几分钟空闲时间后锁定屏幕 我已经尝试过官方手册了在红帽 https access redhat com doc
  • nginx 上的多个网站和可用网站

    通过 nginx 的基本安装 您的sites available文件夹只有一个文件 default 怎么样sites available文件夹的工作原理以及如何使用它来托管多个 单独的 网站 只是为了添加另一种方法 您可以为您托管的每个虚拟
  • chown:不允许操作

    我有问题 我需要通过 php 脚本为系统中的不同用户设置文件所有者权限 所以我通过以下命令执行此操作 其中 1002 是系统的用户 ID file put contents filename content system chown 100
  • 在哪里可以找到并安装 pygame 的依赖项?

    我对 Linux 比较陌生 正在尝试安装 python 的 pygame 开发环境 当我运行 setup py 时 它说我需要安装以下依赖项 我找到并安装了其中之一 SDL 然而 其他人则更加难以捉摸 Hunting dependencie
  • nslookup 报告“无法解析 '(null)': 名称无法解析”,尽管它成功解析了 DNS 名称

    我在 ubuntu 上 并且正在运行 docker 默认桥接网络 我有 Zookeeper kafka 的容器化版本 以及我编写的与 kafka 对话的应用程序 I do a docker exec it
  • arm64和armhf有什么区别?

    Raspberry Pi Type 3 具有 64 位 CPU 但其架构不是arm64 but armhf 有什么区别arm64 and armhf armhf代表 arm hard float 是给定的名称Debian 端口 https
  • 尝试安装 LESS 时出现“请尝试以 root/管理员身份再次运行此命令”错误

    我正在尝试在我的计算机上安装 LESS 并且已经安装了节点 但是 当我输入 node install g less 时 出现以下错误 并且不知道该怎么办 FPaulMAC bin paul npm install g less npm ER
  • 两种情况或 if 哪个更快? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我必须制作一个 非常 轻的脚本 它将接受用户的选项并调用脚本中的函数来执行一些任务 现在我可以使用 IF 和 CASE 选项 但我想知道两
  • 将 PDF 转换为 600dpi 的 TIFF 和 jpg 96 dpi

    我想使用 ImageMagick 从 Python 脚本将 pdf 转换为 600 dpi 的 tiff 和 96 dpi 的 jpg 我使用 imagemagick 命令行完成了这项任务 但我想使用python中的Imagemagick将
  • Linux:在文件保存时触发 Shell 命令

    我想在修改文件时自动触发 shell 命令 我认为这可以通过注册 inotify 挂钩并调用来在代码中完成system 但是是否有更高级别的 bash 命令可以完成此任务 尝试 inotify 工具 我在复制链接时遇到问题 抱歉 但 Git
  • C语言中如何通过内存地址映射函数名和行号?

    如何用 GCC 中的内存地址映射回函数名称和行号 即假设一个 C 语言原型 void func Get the address of caller maybe this could be avoided MemoryAddress get
  • 为arm构建WebRTC

    我想为我的带有arm926ej s处理器的小机器构建webrtc 安装 depot tools 后 我执行了以下步骤 gclient config http webrtc googlecode com svn trunk gclient s
  • PHP 无法打开流:是一个目录

    非常简单的 PHP 脚本 我在我亲自设置的 Ubuntu Web 服务器上的 EE 模板中运行 我知道这与权限有关 并且我已经将我尝试写入的目录的所有者更改为 Apache 用户 我得到的错误是 遇到 PHP 错误 严重性 警告 消息 fi
  • 如何更改 Apache 服务器的根目录? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何更改 Apache 服务器的文档根目录 我基本上想要localhost从 来 users spencer projects目录而不是

随机推荐

  • 使用windeployqt.exe打包QT工程,windows系统可执行程序

    前言 因为自己打包qt程序遇到点问题 提示0xc000007b错误 发现是因为打包工具和工程编译工具不对应导致 于是为了记录打包方法 有了此篇文章 记录使用windeployqt exe打包qt工程在windows系统的可执行文件 一 确定
  • adb install 命令参数

    adb install 6个参数描述 t 允许测试包 l 锁定该应用程序 s 把应用程序安装到sd卡上 g 为应用程序授予所有运行时的权限 r 替换已存在的应用程序 也就是说强制安装 d 允许进行将见状 也就是安装的比手机上带的版本低
  • activiti-serviceTask(服务任务)

    Activiti服务任务 serviceTask Activiti服务任务 serviceTask 作者 邓家海 都有一段沉默的时间 等待厚积薄发 应用场景 当客户有这么一个需求 下一个任务我需要自动执行一些操作 并且这个节点不需要任何的人
  • 一文让你深刻理解异步请求池-DNS解析与实现

    一 DNS概念简述 DNS Domain Name Service 域名解析服务 工作在应用层 是互联网的一项服务 它作为将域名和IP地址相互映射的一个分布式数据库 能够使人更方便地访问互联网 DNS监听在TCP和UDP端口53 FQDN
  • SpringMVC系列(十)(处理静态资源)和...

  • 通俗理解泰勒公式

    本博客只用于自身学习 如有错误 虚心求教 在维基百科上的解释 在数学中 泰勒公式 英语 Taylor s Formula 是一个用函数在某点的信息描述其附近取值的公式 这个公式来自于微积分的泰勒定理 Taylor s theorem 泰勒定
  • 计算方法——C语言实现——迭代法求解线性方程组

    最近在上计算方法这门课 要求是用MATLAB做练习题 但是我觉得C语言也很棒棒啊 题目 和直接法不同 迭代法是一种逐次逼近的方法 将复杂问题简单化 求比较大的方程组时一般都不会用直接法 迭代法有好几种 这里使用了Jacobi迭代与Gauss
  • 8.4收官之战非农蓄力能否引爆黄金单边行情?

    近期有哪些消息面影响黄金走势 黄金多空该如何研判 黄金消息面解析 周五 8月4日 亚洲时段 现货黄金在近三周低位窄幅震荡 目前交投于1937 60美元 盎司附近 美联储7月决策符合预期 如期加息25个基点 虽然美国通胀增速放缓 但仍高于美联
  • Git 大文件push失败

    目录 1 下载并安装Git Large File Storage命令行扩展 2 配置lfs跟踪的文件 3 commit 并push到远程仓库 由于git有push文件的大小限制 100MB 因此如果push操作中右超过100MB的文件 就会
  • 抽签小程序(C语言随机数),C# 抽签小程序

    设计背景 设置一个Excel名单表 对名单进行随机抽取 设计思路 使用Timer定时器 运行定时器进行名单随机滚动 停止定时器获得抽签结果 相关技术 随机数 Excel读取 导出 XML文档读写 相关类库 C1 C1Excel Excel操
  • 《深入浅出话数据结构》系列之什么是B树、B+树?为什么二叉查找树不行?

    本文将为大家介绍B树和B 树 首先介绍了B树的应用场景 为什么需要B树 然后介绍了B树的查询和插入过程 最后谈了B 树针对B树的改进 在谈B树之前 先说一下B树所针对的应用场景 那么B树是用来做什么的呢 B树是一种为辅助存储设计的一种数据结
  • 达梦DCA认证培训和考试

    本人有幸参加了达梦DCA认证培训并参加了认证考试 培训内容包括 第一天 国产数据库现状及未来 DM8企业版安装 创建数据库及数据库实例管理 DM8体系结构 第二天 表空间管理 用户管理 DMSQL 第三天 模式对象管理 备份还原 配置作业
  • 数据结构课程设计 最小生成树,拓扑排序以及最短路径

    通信网络的架设问题 问题描述 若要在n 10 个城市之间建设通信网络 只需要架设n 1条线路即可 如何以最低的经济代价建设这个通信网 是一个网的最小生成树问题 基本要求 1 利用二种方法 Prim算法和克鲁斯卡尔 Kruskual 生成网中
  • 阿里Java后端电话面试

    生平第一次面试 还是阿里 非常紧张 因为是校招 所以面的比较简单 都是我简历上说熟悉的东西 回答的不是很理想 面试官说我广度还行 深度差的比较多 面试官 你好同学 我是蚂蚁金服的 现在方便面试吗 我 方便方便 面试官 请简单介绍一下自己 这
  • Fultter学习日志(2)-构建第一个flutter应用

    依照上一篇中我们新建的flutter应用 让我们更改pubspec yaml中的内容为 name namer app description A new Flutter project publish to none Remove this
  • 在 Python Lambda 中使用 Await

    异步编程不是多线程或多进程 相反 它是并发编程 我们可以运行一个可能长时间运行的任务 并允许我们的程序在该任务仍在运行时响应其他任务 而不是等待完成 对于异步编程 HTTP 请求操作或用户选择可能会花费大量时间 因此 允许其他任务在这些操作
  • 100天精通Python(数据分析篇)——第72天:Pandas文本数据处理方法之判断类型、去除空白字符、拆分和连接

    文章目录 每篇前言 一 Python字符串内置方法 1 判断类型 2 去除空白字符 3 拆分和连接 二 Pandas判断类型 1 str isspace 2 str isalnum 3 str isalpha 4 str isdecimal
  • mysql存储过程和存储函数

    一 存储过程概述 1 mysql存储过程和存储函数是将复杂sql集合在一起 应用程序只需调用即可 不必关注mysql存储过程和存储函数sql逻辑 存储过程预先经过编译的一组sql 存储在 MySQL 服务器上 需要执行的时候 客户端只需要向
  • go 递归tree关系_统一的树可视化形式描述语法 – GoTree

    树可视化是可视化领域长期以来的研究热点 近40年来 研究者利用不同的视觉映射方式发展了超过300种树可视化形式 https treevis net 并且广泛应用在日常生活中 例如展示电脑文件目录结构的缩进列表 反映股市中公司市值以及股价升降
  • 【Linux】网络基础(二)

    TCP UDP协议 UDP 协议通信程序的编写 套接字相关接口介绍 字节序相关接口 IP 地址转换接口 TCP 协议通信程序的编写 套接字接口介绍 多进程实现多执行流 多线程实现多执行流 UDP 协议通信程序的编写 UDP 协议 无连接 不