端口号
IP地址(确定主机)+端口号(在机上的应用程序)
网络层提供的概念 传输层提供的概念
一个字节:
有符号-128=>+127
无符号0=>255
两个字节:
有符号:-32768=>+32767
无符号0=>65535
四个字节:
有符号:-21亿=>+21亿
无符号:0=>42亿9千万
同一个机器上,同一时刻内,端口号不能重复被绑定。
某个进程A绑定了10000端口号,此时进程B也尝试绑定10000端口,此时B就会绑定失败!
会提示Address already in use。
可以在服务命令行中输入netstat -ano查看当然那些进程正在使用
netstat -ano|findstr 9090 查询出当前主机上是否有使用9090端口的进程
|指管道把多个命令组合起来。
两个进程不能绑定同一个端口号,好比"一山不能容二虎,除非一公一母。
如果一个服务器是TCP,一个是UDP此时,端口重复了,是不影响的(一公一母)但是如果两个TCP/两个UDP,使用同一个端口,就会出现上述绑定失败的情况了。
java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Executable;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpEchoServer {
private ServerSocket serverSocket=null;
private ServerSocket serverSocket1=null;
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverSocket1= new ServerSocket(port+1);
}
public void start() throws IOException {
System.out.println("服务器启动");
ExecutorService pool= Executors.newCachedThreadPool();
while(true) {
Socket socket = serverSocket.accept();
// Thread t=new Thread(()->{
// try {
// processConnection(socket);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// });
// t.start();
pool.submit(new Runnable (){
public void run() {
try {
processConnection(socket);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
}
public void processConnection(Socket socket) throws IOException {
System.out.printf("[%s:%d]客户端上线!\n",socket.getInetAddress(),socket.getPort());
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()){
Scanner scanner = new Scanner(inputStream);
while (true){
if(!scanner.hasNext()){
System.out.printf("[%s:%d]客户端下线!\n",socket.getInetAddress(),socket.getPort());
break;
}
String request = scanner.next();
String response=process(request);
outputStream.write(response.getBytes()); ;
System.out.println(response);
}
}catch ( Exception e ){
e.printStackTrace();
}finally {
socket.close();
}
}
public String process(String request) {
return request+"\n";
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}
这样就服务器有两个端口号了。
一个进程绑定多个端口,用途非常广泛的,未来工作中经常能见到
比如写一个服务器程序,首先服务器需要有一个端口号,给客户端提供业务功能.这样的端口,称为"业务端口
程序猿还需要能够对这个服务器进行更精细的控制.
比如控制让这个服务器重新加载配置/开启某个功能/重新启动/重新加载数据/修改某个选项设定...
这样的操作,经常会通过网络来进行操作.服务器就会另外绑定一个端口号,称为"管理端口"
程序员想对这个服务器进行管理操作,就通过管理端口给服务器发送一些对应的请求,服务器执行对应的逻辑
日常开发会遇到一些bug,需要去查看服务器的一些运行状态(比如服务器中的一些关键的变量是啥样的值...服务器不能直接用调试器去调试~~(调试器一调试就会把服务器阻塞住,无法给别的客户端提供服务了)
也会通过网络的方式,给服务器发调试请求,服务器返回对应的关键信息.称为"调试端口"。
UDP
学习一个网络协议,最主要就是学习的报文格式。
UDP数据报=UDP报头+UDP载荷
UDP的长度为2个字节所以UDP最长为65536(64kb).
既然UDP有上述限制,为啥发明UDP的大佬不对UDP做出改进升级呢?比如把报头中报文长度字段改成4字节,或者更长呢?
世界上任何一个主机,都可能是发送端也都可能是接收端要升级,就得全世界所有的主机都一起升级~~
相比于对UDP升级,未来诞生新的协议,取代UDP,可能是更靠谱一些的~
UDP数据报中,载荷最多能承载多少数据??
这里我们回答的仍然是64kb!!!
可以忽略不计。
UDP校验和
比特翻转
本来你传输的是0,实际到了对端变成1了.或者本来传的是1,到了对端变成0了,
受外界环境干扰导致信息改变。
第一层能够发现是否出错
第二层最好能发现是哪一位出错,并且能够进行纠错
在UDP中,校验和只能够做到第一层,发现是否有错。
校验和出错/数据内容出错
最终在B这边感受到的都是就算出来的校验和2和收到的校验和不~
CRC (循环冗余校验)
设定2个字节的变量,把数据的每个字节取出来,往这个变量上进行累加如果结果溢出超过2个字节,溢出部分就舍弃~~
MD
定长:无论输入的内容是多长,得到的结果,一定是固定长度.
分散:输入的内容,哪怕只改变一点点,最终结果都会差异很大(比特翻转,往往只是变化了一点点).
不可逆:通过原数据,计算md5,成本很低;直到md5,还原成原数据,成本非常高,仅仅理论上可行.
UDP现在最主要的用途
应用于对于性能要求比较高,但是对于可靠性要求不高的场景