java的UDP(二)

news2024/11/28 11:01:08

文章目录

  • 1. DatagramSocket类
  • 2. 简单的UDP客户端
  • 3. DatagramChannel

1. DatagramSocket类

要收发DatagramPacket,必须打开一个数据报Socket。在java中,数据报Socekt通过DatagramSocekt类创建和访问。服务器Socket需要指定绑定端口,而用户端Socket不用关心(其他方面两个没有什么区别)。

  • 构造函数

DatagramSocekt构造函数用于不同的情况,这与DatagramSocket构造函数类似。

public DatagramSocket() throws SocketException

在匿名本地端口打开一个数据报Socket,一般用于客户端。用户不用关心端口,服务器会将响应发送到发出数据报的端口。让系统分配一个端口。如果出于某些原因需要知道本地端口,可以使用getLocalPort()方法得到。

public DatagramSocket(int port) throws SocektException

这个构造函数创建一个在指定端口监听入站数据报的Socket。TCP端口和UDP端口没有任何关联。对于两个不同的程序,如果一个使用UDP而另一个使用TCP,那么它们可以使用相同的端口号。

public DatagramSocket(int port,InetAddress interface) throws SocketException

创建一个绑定到指定端口和指定网络接口的DatagramSocket对象

public DatagramSocket(SocketAddress interface)throws SocketException

这个构造函数和前一个相似就,只是网络接口地址和端口,由SocektAddress读取。

protected DatagramSocket(DatagramSocketImpl impl) throws SocketException

这个构造函数允许子类提供自己的UDP实现,而不是默认实现。与其他4个构造函数创建的Socket不同,这个Socket一开始没有与端口绑定。使用前必须啊通过bind()方法绑定到一个SocketAddress。可以向这个方法传递null,将Socket绑定到任何可用的地址和端口

  • 发送和接受数据报

DatagramSocket类的首要任务是发送和接受UDP数据,一个Socket既可以发送又接收数据报,事实上,它可以和多台主机收发数据。

public void send(DatagramPacket dp) throws IOException

下面是一个基于UDP的discard客户端。它从System.in读取用户输入的行就,将其发送给discard服务器,这个服务器只是丢弃所有数据。每一行都填充在一个DatagramPacket中。

 public static void main(String[] args)  {
         try(DatagramSocket thesocket=new DatagramSocket()){
            InetAddress server=InetAddress.getByName("localhost");
            BufferedReader userInput=new BufferedReader(new InputStreamReader(System.in));
            while(true){
                String theline=userInput.readLine();
                if(theline.equals("."))break;
                byte[] data=theline.getBytes();
                DatagramPacket thoutput=new DatagramPacket(data,data.length,server,8080);
                thesocket.send(thoutput);
            }
        } catch (SocketException | UnknownHostException e) {
             throw new RuntimeException(e);
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
    }
public void receive(DatagramPacket dp)throws IOException

这个方法从网络中接收一个UDP数据报,存储在现有的DatagramPacket对象dp中。与ServerSocket类的accept方法类似,这个方法会阻塞调用线程,直到数据报到达。如果程序除了等待数据报外还有其他操作,就应当在单独线程中调用receive方法

public void close()

调用DatagramSocket对象的close方法将释放该Socket占用的端口。与流和TCP Socket一样。

public int getLocalPort()

DatagramSocket的getLocalPort()方法返回一个int,表示Socket正在监听的本地端口。如果我们创建DatagramSocket时是系统帮我们选定的端口,此时可以使用这个方法查看系统给我们分配的是什么端口。

public InetAddress getLocalAddress()

DatagramSocket的getLocalAddress() 方法返回一个InetAddress对象,表示Socekt绑定到的本地地址。实际中很少需要这样做。

public SocketAddress getLocalAddress()

该方法返回一个SocketAddress对象,这个对象包装了Socket绑定的本地接口和端口。实际用处也不大

  • 管理连接

与TCP socket不同,数据报Socket不太在意与谁对话。事实上,默认情况下它们可以与任何人对话,但这通常不是你希望的。

public void connect(InetAddress host, int port)

connect()方法并不真正建立TCP意义上的连接。不过,它确实指定了DatagramSocket只对指定远程主机和指定远程端口收发数据包。试图向其他主机发送数据包将抛出一个IllegalArgumentException异常。从其它的主机或其他的主机或其他的端口接收的数据报将被丢弃,没有异常,也没有通知。

public void disconnect()

disconnect()方法中断已连接DatagramSocket的“连接”,从而可以再次收发任何主机和端口的包。

public int getPort()

当且仅当DatagramSocket已连接时,getPort()方法返回它所连接的远程端口。否则返回-1.

public InetAddress getInetAddress()

当且仅当DatagramSocket已连接时,getInetAddress()方法返回它所连接的远程主机的地址。否则返回null

public InetAddress getRemoteSocketAddress()

如果DatagramSocket已连接,该方法返回它所连接的远程主机的地址

  • Socket选项

Java支持6个UDP Socket选项:

  1. SO_TIMEOUT
  2. SO_RCVBUF
  3. SO_SNDBUF
  4. SO_REUSEADDR
  5. SO_BROADCAST
  6. IP_TOS

SO_TIMEOUT
是receive()在抛出InterruptedIOException(IOException 的一个子类)异常前等待入站数据报的时间,以毫秒计算。可以用下面两个方法设置和查看

public void setSoTimeout(int timeout) throws SocketException
public int getSoTimeout() throws IOException

SO_RCVBUF
DatagramSocket的SO_RCVBUF选项与Socket的SO_RCVBUF选项紧密相关。它确定了用于网络I/O的缓冲区大小。对于相当快的连接(如以太网速的连接),较大的缓冲区有助于提升性能,因为在溢出前可以存储更多的入站数据报。与TCP相比,对于UDP,足够大的接收缓冲区甚至更加重要,因为在缓冲区满时到达的的UDP数据报就会丢失,而缓冲区满时到达的TCP数据报最后还会重传。此外,SO_RCVBUF设置了应用程序可以接受的数据报包的大小,接收的缓冲区中放不下的包会不声不响地被丢弃掉。

//它只是建议,具体的底层实现可以忽略这个建议
public void setReceiveBufferSize(int size) throws SocketException
public void getReceiveBufferSize() throws SocketException

如果底层Socket实现不能识别SO_RCVBUF选项,这两个方法都会抛出SocketException异常

SO_REUSEADDR

SO_REUSEADDR选项对于UDP Socket的意义与对于TCP Socket的意义有所不同,对于UDP,该选项可以控制是否允许多个数据报Socket同时绑定到相同端口和地址。如果多个Socket绑定到相同端口,接收的包将复制给绑定的所有Socket

public void setReuseAddress(boolean on) throws SocketException
public boolean getReuseAddress() SocketException

SO_BROADCAST
该选项控制是否允许一个Socket向广播地址收发包,如广播地址192.168.254.255。UDP广播常用于DHCP等协议。

public void setBroadcast(boolean on) throws SocketException
public boolean getBroadcast() throws SocketException

IP_TOS
由于业余流类型由多个IP数据报首部中的IP_TOS字段值来确定,所以它对于UDP与对于TCP同样重要,毕竟包要根据IP地址进行路由和区分优先级,而TCP和UPD都建立在IP基础之上。DatagramSocket的setTrafficClass()和getTrafficClass()方法与Socket中相应方法实际上没有分别。之所以必须在这里重复出现,只是因为DatagramSocket和Socket没有共同的超类

public int getTrafficClass() throws SocketException
public void setTrafficClass() throws SocketException

trafficClass参数是一个整数,表示要设置的流量类别。具体的数值取决于操作系统和网络设备的支持,常见的取值如下:

  • 最高两位(DSCP字段):
    • 0x00:Best Effort
    • 0x08:Express Forwarding
    • 0x10:Assured Forwarding
    • 0x18:Voice-Admit Forwarding
  • 后六位(ECN字段):
    • 0x00:Non-ECN
    • 0x01:ECT (0)
    • 0x02:ECT (1)
    • 0x03:CE (Congestion Encountered)

2. 简单的UDP客户端

一些Internet服务只需要知道客户端的地址和端口,它们会忽略客户端在数据报中发送的数据。所以下面实现一个UDPock简单额客户端

public class QuizCardBuilder {
    //希望从服务器接收到的数据报的大小
    private int bufferSize;
    //等待入站数据报超时时间
    private int timeout;
    private InetAddress host;
    private int port;
    public QuizCardBuilder(InetAddress address,int port , int bufferSize , int timeout){
        this.bufferSize=bufferSize;
        this.host=address;
        this.port=port;
        this.timeout=timeout;
    }

    public  QuizCardBuilder(InetAddress address, int port, int bufferSize){
        //没有指定超时时间的话就默认设置为30s
        this(address, port , bufferSize ,30000);
    }
    public QuizCardBuilder(InetAddress address , int port){
        //如果没有指定缓冲区的大小,就将缓冲区大小指定为8192
        this(address, port, 8192, 30000);
    }
    public byte[] poke(){
        try(DatagramSocket socket=new DatagramSocket(0)){
            DatagramPacket outgoing= new DatagramPacket(new byte[1] , 1 ,host ,port);
            socket.connect(host,port);
            socket.send(outgoing);
            DatagramPacket incoming=new DatagramPacket(new byte[bufferSize],bufferSize);
            socket.receive(incoming);
            int numBytes=incoming.getLength();
            byte[] response=new byte[numBytes];
            System.arraycopy(incoming.getData(),0,response,0,numBytes);
            return  response;
        } catch (SocketException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args)  {
        InetAddress host;
        int port=0;
        try{
            host=InetAddress.getByName("time.nist.gov");
            port=Integer.parseInt("13");
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
       try{
           QuizCardBuilder quizCardBuilder=new QuizCardBuilder(host,port);
           byte[] response=quizCardBuilder.poke();
           if(response==null){
               System.out.println("No response within allotted time");
               return;
           }
           String result=new String(response, "US-ASCII");
           System.out.println(result);
       } catch (UnsupportedEncodingException e) {
           throw new RuntimeException(e);
       }
    }
}

3. DatagramChannel

该类用于非阻塞的UDP应用程序,就是SocketChannel和ServerSocketChannel用于非阻塞的TCP应用程序一样。类似于SocketChannel和ServerSocketChannel,,DatagramChannel是SelectChannel的子类,可以注册到一个Selector。如果服务器中一个线程可以管理与多个不同客户端的通信,该类就很有用。不过,UDP天生就比TCP更具异步性,因而实际效果没有那么明显。在UDP汇总,一个数据报Socket可以处理多个客户端的输入和输出请求。该类所增加的就是以非阻塞方式来做到这一点,这样一来,如果网络没有立即准备好发送数据,这些方法就可以迅速返回

  • 打开一个Socket

该类没有公共构造函数,要使用静态方法open创建一个新的DatagramChannel对象

DatagramChannel channel= DatagramChannel.open()

这个通道开始时没有绑定到任何端口,在绑定端口,需要使用Socket()方法访问该通道的对等DatagramSocket对象。例如,下面的代码把通道绑定到3141:

SocketAddress address=new InetSocketAddress(3134);
DatagramSocket socket=channle.socket();
socket.bind(address);

Java 7提供了一个更加方便的方法

SocketAddress address=new InetSocketAddress(3134);
channel.bind(address);
  • 接收

receive方法从通道读取一个数据包,放在一个ByteBuffer中。它返回发送这个包的主机地址

publoic SocketAddress receive(ByteBuffer dst) throws IOException

如果通道是阻塞的,该方法在读取到包之前不会返回,如果通道是非阻塞的,没有包可以读取的情况下这个方法会立即返回null。如果数据报的数据超过了缓冲区的大小,多余的部分会被丢弃。

public static void main(String[] args){
	DatagramChannel channel =DatagramChannel.open();
	DatagramSocket socket=channel.socket();
	SocketAddress address= new InetSocketAddress(PORT);
	socket.bind(address);
	ByteBuffer buffer=ByteBuffer.allocateDirect(65507);
	while(true){
		SocketAddress client= channel.receive(buffer);
		buffer.flip();
		System.out.println(client+"says");
		while(buffer.hasRemainding()) System.out.write(buffer.get);
		System.out.println();
		buffer.close();
	}
}catch(IOException ex){
	System.err.println(ex);
}
	
  • 发送

send()方法将一个数据报从ByteBuffer写入通道,要写到由第二个参数指定的地址:

public int send(ByteBuffer src, SocketAddress target) throws IOException

send方法返回写入的字节数,这可能是要写的缓冲区中的可用的字节数,也可能是0,而不会是其他值,如果通道出于非阻塞模式,而且数据不能立即发送,就会返回0,否则,如果通道不在非阻塞模式,send会等待返回,知道他能发送缓冲区中的全部数据。

public static void main(String[] args){
	DatagramChannel channel=DatagramChannel.open();
	DatagramSocket address=channel.socket();
	SocketAddress address=new InetSocketAddress(7);
	socket.bind(address);
	ByteBuffer buffer=ByteBuffer.allocateDirect(65507);
	while(true){
		SocketAddress client=channel.receive(buffer);
		buffer.flip();
		channel.send(buffer,client);
		buffer.clear()
	}
}catch(IOException ex){
	System.err.println(ex);
}
  • 连接

一旦打开一个数据报通道,可以使用connect()方法,将它连接到一个特定的远程地址:

SocketAddress remote=new InetSocketAddress("time.nist.gov",37)

通道只向这个主机发送数据,或者只从这个主机接收数据,与SocketChannel的Connect()方法不同,这个方法本身不在网络上收发任何包,因为UDP是一种无连接协议。它只是建立一个主机,有数据准备好可以发送时,就会向这个主机发送数据包。因此,这个方法会相当快的返回,不会有任何方面阻塞。它有一个isConnected()方法,当且仅当DatagramSocket连接时,它会返回true。还有一个disconnect断开连接

  • 读取

除了用于特殊用途的receive方法,DatagramChannel还有3个一般的read()方法

public int read(ByteBuffer dst) throws IOException
public long read(ByteBuffer[] dsts) throws IOException
public read(ByteBuffer[] dsts, int offset ,int length) throws IOException

不过这些方法只能用于已连接的通道。也就是说,在调用这些方法之前。必须调用connect将通道连接到某个远程主机快,这使得这些方法适用客户端使用,因为它知道自己和谁在通信,而服务器可能需要和多个客户端通信。者3个方法都只从网络读取一个数据报包。数据报中的数据尽可能多地存储在参数ByteBuffer中。每个方法都会返回读取的字节数,或者如果通道关闭,则返回-1,如果出现羡慕的情况,这个方法会返回0

  1. 通道是非阻塞的,而且没有就绪的包
  2. 数据报中不包含任何数据
  3. 缓冲区已满
  • 写入
    很自然的DatagramChannel有3个write方法,可有可写、散布的通道有这3个方法写入,它们可以用来替代send方法
public int write(ByteBuffer src)throws IOException
public long write(ByteBuffer[] dsts) throws IOException
public long write(ByteBuffer[] dsts, int offset, int length)

同样这些方法也只能用于已经连接的通道。

public class UDPEchoClientWithChannels{
	public final static int PORT=7;
	private final static int LIMIT=100;
	public static void main(String[] args){
		SocketAddress remote;
		try{
			remote=new InetSocketAddress(args[0], PORT);
		}catch(RuntimeException ex){
		return;
		}
		try(DatagramChannel channel=DatagramChannel.open()){
		//开启非阻塞模式
			channel.configureBlocking(false);
			cahnnel.connect(remote);

			Selector selector=Selector.open();
			channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

			ByteBuffer buffer= ByteBuffer.allocote(4);
			int n=0;
			int numbersRead=0;
			while(true){
				if(numbersRead==LIMIT)break;
				//为一个连接等待1分钟
				selector.select(60000);
				Set(SelectionKey> readyKeys= selector.selectKeys();
				if(readyKeys.isEmpty() && n==LIMIT){
					//所有的包已经写入
					//好像不会再有更多数据从网络到达
					break;
				}else{
				  Iterator<SelectionKey> iterator=readyKeys.iterator();
				  while(iterator.hasNext()){
				  	SelectionKey key=(SelectionKey) iterator.next();
				  	iterator.remove();
				  	if(key.isReachable){
				  		buffer.clear();
				  		channel.read(buffer);
				  		buffer.flip();
				  		int echo=buffer.getInt();
				  		System.out.println("Read:"+echo);
				  		numbersRead++;
				  	}
				  	if(key.isWritable()){
				  		buffer.clear();
				  		buffer.putInt(n);
				  		buffer.flip();
				  		channel.write(buffer);
				  		System.out.println("Wrote:"+n);
				  		n++;
				  		if(n==LIMIT){
				  			key.interestOps(SelectionKey.OP_READ);
				  		}
				  	}
				  }
				}
		}
	}catch(IOException ex){
	}
}
}
  • 关闭

就像常规的Socket一样,应当在结束操作时关闭通道,释放它使用的端口和任何其他资源

public void close() throws IOException

关闭已关闭的通道没有任何效果。试图向已关闭的通道写入或读取数据会抛出异常。如果不确定一个通道是否关闭可以使用isOpen方法查看。与所有通道一样,在Java 7中实现了AutoCloseable。

try(DatagramChannel channel= DatagramChannel.open()){
	
} catch(IOException e){

}

在java 7 以后版本中,DatagramChannel支持8个Socket选项

在这里插入图片描述

前5个选项与之前介绍的Socket选项一致。后3个会在IP组播中使用。这些选项可以用3个方法来检查和设置

//改变选项值
public<T> DatagramChannel setOption(SocketOption<T> name ,T value)throws IOException
//指出任意一个选项的当前值
public<T> T getOption(SocketOption<T> name) throws IOException
//会列出所有的可用的Socket选项
public Set<SocketOption<?>> supportedOptions()
public class QuizCardBuilder {
    public static void main(String[] args)  {
       try(DatagramChannel channel= DatagramChannel.open()){
           for(SocketOption<?> option: channel.supportedOptions()){
               System.out.println(option.name()+":"+channel.getOption(option));
           }
       } catch (IOException e) {
           throw new RuntimeException(e);
       }
    }
}

在这里插入图片描述

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

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

相关文章

【react框架】结合antd做表单组件的一些心得记录

文章目录 前言功能的实现尽量先看看antd上是否已经提供当一个页面有多个表单组件时&#xff0c;就要优先考虑把值存在状态管理中如果一些表单比较简单且能确保后续不会有功能上的拓展&#xff0c;可以使用业务组件推荐其他的表单库 前言 因为最近在学其他东西&#xff0c;今天…

华中科技大学计算机考研分析

关注我们的微信公众号 姚哥计算机考研 更多详情欢迎咨询 华中科技大学计算机考研招生学院是计算机科学与技术学院、软件学院和网络空间安全学院。目前均已出拟录取名单。 华中科技大学计算机科学与技术学院成立于1997年&#xff0c;其前身是原华中理工大学&#xff08;即华中…

2023智源大会议程公开丨AI开源论坛

6月9日&#xff0c;2023北京智源大会&#xff0c;将邀请这一领域的探索者、实践者、以及关心智能科学的每个人&#xff0c;共同拉开未来舞台的帷幕&#xff0c;你准备好了吗&#xff1f;与会知名嘉宾包括&#xff0c;图灵奖得主Yann LeCun、图灵奖得主Geoffrey Hinton、OpenAI创…

chatgpt赋能python:Python反转字符串——简单易学的编程技巧

Python反转字符串——简单易学的编程技巧 你是否曾经遇到过需要反转字符串的情况&#xff1f;例如&#xff0c;你想把“Hello World”转换成“dlroW olleH”&#xff1f;如果是这样&#xff0c;Python可以帮你完成这个任务。在本文中&#xff0c;我们将看到如何使用Python的简…

C++标准模板库 栈容器的使用

我们知道c标准模板库中栈和队列很重要&#xff0c;是考试的重点内容&#xff0c;今天和我一起来学习栈容器的使用吧 特点&#xff1a;先进后出&#xff0c;后进先出&#xff08;餐馆的盘子&#xff09; C中有专门的栈容器stack 常用 Push(S,x);//入栈 POp(S,x);//出栈 GetT…

判断点在多边形内算法的C++实现

本篇博客介绍了使用射线法判断点在多边形内部还是外部的算法&#xff0c;并通过C做了具体实现 1. 算法思路 判断平面内点是否在多边形内有多种算法&#xff0c;其中射线法是其中比较好理解的一种&#xff0c;而且能够支持凹多边形的情况。该算法的思路很简单&#xff0c;就是…

网络基础(二)- TCP协议

TCP协议 TCP全称为 “传输控制协议(Transmission Control Protocol”)。它最重要的是解决在传输层通信的过程中&#xff0c;解决网络通信过程中可靠性的问题。当然&#xff0c;很多人在理解TCP的时候&#xff0c;往往只知道帮我们解决可靠性&#xff0c;但是&#xff0c;同时呢…

JavaWeb三大组件之一Filter(过滤器)

定义 拦截所有访问web资源的请求或者响应(servlet、Jsp页面、HTML页面)&#xff0c;从而实现我们自己的业务逻辑&#xff0c;这些逻辑可以是实现访问权限的控制、过滤敏感词、压缩响应等功能。 原理 过滤器是"链接"在容器的处理过程中的&#xff0c;它会在servle…

2023 华为 Datacom-HCIE 真题题库 09/12--含解析

单项选择题 1.[试题编号&#xff1a;190485] &#xff08;单选题&#xff09;华为交换机MAC地址表的老化时间默认是多少秒? A、500 B、5 C、300 D、400 答案&#xff1a;C 解析&#xff1a;无 2.[试题编号&#xff1a;190484] &#xff08;单选题&#xff09;如图所示&#…

小伙以1.2万元价格拍下一颗“荔枝王”:要将这颗最好的荔枝送给女友

近日&#xff0c;一则海南海口公益拍卖的新闻引起了网友的热议。 一位小伙子以1.2万元的价格拍下了一颗被誉为“荔枝王”的稀有果实&#xff0c;并表示要将这颗最好的荔枝送给自己的女友。 这个举动引起了一些网友的质疑&#xff0c;有人称赞他的浪漫&#xff0c;也有人质疑他的…

免费分享一套 SpringBoot + Vue的排课/选课管理系统,挺漂亮的

大家好&#xff0c;我是锋哥&#xff0c;看到一个不错的SpringBoot Vue 的排课/选课管理系统&#xff0c;分享下哈。 项目介绍 近年来&#xff0c;随着网络学校规模的逐渐增大&#xff0c;人工书写数据已经不能够处理如此庞大的数据。为了更好的适应信息时代的高效性&#x…

Nginx网络服务——虚拟主机设置

Nginx网络服务——虚拟主机设置 一、基于域名的 Nginx 虚拟主机1&#xff0e;为虚拟主机提供域名解析2.为虚拟主机准备网页文档3.修改Nginx的配置文件4.重启服务&#xff0c;访问测试 二、基于IP 的 Nginx 虚拟主机1.设置虚拟主机IP2.修改主配置文件3.重启服务&#xff0c;访问…

chatgpt赋能python:Python反向传播算法——神经网络的基础

Python反向传播算法——神经网络的基础 什么是反向传播算法&#xff1f; 反向传播算法是神经网络中最常用的学习算法之一。它的作用是根据输入数据和输出数据来调整神经网络中的权重&#xff0c;从而让神经网络模型更好地适应特定的问题。 简单来说&#xff0c;反向传播算法…

强化学习Q-learning入门

1. 引言 本文为最近学习的强化学习Q-learning的学习笔记&#xff0c;主要用于总结和日常记录&#xff0c;本文主要讲解相应的必备入门知识。 闲话少说&#xff0c;我们直接开始吧&#xff01; 2. 概念 我们小时候都经历过以下情形&#xff1a;我们做错了某年事&#xff0c;…

string

1.标准库的string类 构造函数&#xff1a; 构造函数的使用&#xff1a; 析构函数&#xff1a; 编译器会自己进行调用&#xff0c;不用管 拷贝构造函数&#xff1a; 追加字符函数&#xff1a; 追加字符串函数&#xff1a; 运算符重载函数&#xff1a; &#xff1a; 会用之后…

Python之socket编程(53)

​小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 今天的内容比较多哦&#xff0c;主要是网络编程的一些入门知识。 这些知识在互联网发展过程中很重要&#xff0…

Java中final关键字的理解(多线程中的final)

一、final关键字 Java中的final关键字具有不可变性的特性&#xff0c;其可以修饰类、方法和变量。 1. final修饰类&#xff1a;final关键字可以用来修饰类&#xff0c;表示该类是不可继承的。final类中的所有方法都是隐式final的&#xff08;不能被override或shadow&#xff0…

【深度学习-第1篇】深度学习是什么、能干什么、要怎样学?

大概4年前本专栏发布了一篇关于神经网络的入门科普文章&#xff0c;到现在大概有小5k赞了&#xff08;Mr.看海&#xff1a;神经网络15分钟入门&#xff01;足够通俗易懂了吧&#xff09;。 这几年人工智能发生了突飞猛进的发展&#xff0c;时至今日甚至发展出了GPT-4、Midjourn…

经典高并发服务器设计逻辑

都是面试必问的八股&#xff0c;不管理不理解用不用得上&#xff0c;背就完事了。 服务器模型 对于并发量比较大的服务器&#xff0c;即listen监听端口一直忙碌于处理新建连接的场景&#xff0c;一般在主线程里面accept新的客户端连接并生成新连接的socket&#xff0c;然后将这…

Unity制作二次元卡通渲染角色材质——2、色阶化光影的多种做法对比

Unity制作二次元材质角色 回到目录 大家好&#xff0c;我是阿赵。 这里继续讲二次元角色渲染。之前说过&#xff0c;最基本的卡通渲染&#xff0c;包含了色阶化光影和描边二个元素。所以这里先来说一下色阶化光影的多种做法对比。 一、光照模型和色阶化的说明 从上一篇文章里…