IP 和 端口号

一、网络编程中有两个主要的问题:


1.如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
2.找到主机后如何可靠高效地进行数据传输


二、网络编程中的两个要素:


1.对应问题一:IP和端口号
2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)

三、通信要素一:IP和端口号


1. IP:唯一的标识 Internet 上的计算机(通信实体)
2. 在Java中使用InetAddress类代表IP
3. IP分类:IPv4 和 IPv6 ; 万维网 和 局域网
4. 域名: www.baidu.com www.mi.com www.sina.com www.jd.com
www.vip.com
5. 本地回路地址:127.0.0.1 对应着:localhost

  1. 如何实例化InetAddress:两个方法:getByName(String host) 、 getLocalHost()
    两个常用方法:getHostName() / getHostAddress()
    image.png

    1. public static void main(String[] args) {
    2. try {
    3. //getByName(String host)
    4. InetAddress inet1 = InetAddress.getByName("192.168.10.14");
    5. System.out.println(inet1);// /192.168.10.14
    6. InetAddress inet2 = InetAddress.getByName("www.fawdezjb.asia");
    7. System.out.println(inet2);//www.fawdezjb.asia/1.117.41.243
    8. InetAddress inet3 = InetAddress.getByName("127.0.0.1");
    9. System.out.println(inet3);// /127.0.0.1
    10. //获取本地ip
    11. InetAddress inet4 = InetAddress.getLocalHost();
    12. System.out.println(inet4);//zj-xtyw06/10.72.113.22
    13. //getHostName()
    14. System.out.println(inet2.getHostName());//www.fawdezjb.asia
    15. //getHostAddress()
    16. System.out.println(inet2.getHostAddress());//1.117.41.243
    17. //isReachable(int timeout):测试是否可以达到该地址
    18. System.out.println(inet2.isReachable(1000));//true
    19. } catch (UnknownHostException e) {
    20. e.printStackTrace();
    21. }catch (IOException e) {
    22. e.printStackTrace();
    23. }
    24. }


    7. 端口号:正在计算机上运行的进程。
    要求:不同的进程有不同的端口号
    范围:被规定为一个 16 位的整数 0~65535。

  2. 端口号与IP地址的组合得出一个网络套接字:Socket

    TCP&UDP

    三次握手与四次挥手

    握手是建立连接,挥手是结束连接
    序号:表示发送的数据字节流,确保TCP传输有序,对每个字节编号(Sequence:序号)
    确认序号:发送方期待接收的下一序列号,接收成功后的数据字节序列号加 1。只有ACK=1时才有效。(Acknowledgement:确认)
    image.png
    src=http___images1.tqwba.com_20200403_l3frnudus4i.png&refer=http___images1.tqwba[1].jpg
    (1)首先客户端向服务器端发送一段TCP报文,其中:

  • 标记位为SYN,表示“请求建立新连接”;
  • 序号为Seq=X(X一般为1);
  • 随后客户端进入SYN-SENT阶段。

(2)服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,其中:

  • 标志位为SYN和ACK,表示“确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);
  • 序号为Seq=y;确认号为Ack=x+1,表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值;
  • 随后服务器端进入SYN-RCVD阶段。

(3)客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:

  • 标志位为ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);
  • 序号为Seq=x+1,表示收到服务器端的确认号Ack,并将其值作为自己的序号值;
  • 确认号为Ack=y+1,表示收到服务器端序号Seq,并将其值加1作为自己的确认号Ack的值;
  • 随后客户端进入ESTABLISHED阶段。

服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。
src=http___img-blog.csdnimg.cn_img_convert_bbdbeab7aac1f904b223f4493a4650e0.png&refer=http___img-blog.csdnimg[1].jpg
1)首先客户端想要释放连接,向服务器端发送一段TCP报文,其中:

  • 标记位为FIN,表示“请求释放连接“;
  • 序号为Seq=U;
  • 随后客户端进入FIN-WAIT-1阶段,即半关闭阶段。并且停止在客户端到服务器端方向上发送数据,但是客户端仍然能接收从服务器端传输过来的数据。

注意:这里不发送的是正常连接时传输的数据(非确认报文),而不是一切数据,所以客户端仍然能发送ACK确认报文。
(2)服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,随后服务器端结束ESTABLISHED阶段,进入CLOSE-WAIT阶段(半关闭状态)并返回一段TCP报文,其中:

  • 标记位为ACK,表示“接收到客户端发送的释放连接的请求”;
  • 序号为Seq=V;
  • 确认号为Ack=U+1,表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值;
  • 随后服务器端开始准备释放服务器端到客户端方向上的连接。客户端收到从服务器端发出的TCP报文之后,确认了服务器收到了客户端发出的释放连接请求,随后客户端结束FIN-WAIT-1阶段,进入FIN-WAIT-2阶段

前”两次挥手”既让服务器端知道了客户端想要释放连接,也让客户端知道了服务器端了解了自己想要释放连接的请求。于是,可以确认关闭客户端到服务器端方向上的连接了
(3)服务器端自从发出ACK确认报文之后,经过CLOSED-WAIT阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文,其中:

  • 标记位为FIN,ACK,表示“已经准备好释放连接了”。注意:这里的ACK并不是确认收到服务器端报文的确认报文。
  • 序号为Seq=W;
  • 确认号为Ack=U+1;表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值。

随后服务器端结束CLOSE-WAIT阶段,进入LAST-ACK阶段。并且停止在服务器端到客户端的方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。
(4)客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文,其中:

  • 标记位为ACK,表示“接收到服务器准备好释放连接的信号”。
  • 序号为Seq=U+1;表示是在收到了服务器端报文的基础上,将其确认号Ack值作为本段报文序号的值。
  • 确认号为Ack=W+1;表示是在收到了服务器端报文的基础上,将其序号Seq值作为本段报文确认号的值。

随后客户端开始在TIME-WAIT阶段等待2MSL

TCP编程

例子1:客户端发送信息给服务端,服务端将数据显示在控制台上

  1. public void client() {
  2. Socket socket = null;
  3. OutputStream os = null;
  4. try {
  5. //1.创建Socket对象,指明服务器端的ip和端口号
  6. InetAddress inet = InetAddress.getByName("10.72.113.22");
  7. socket = new Socket(inet,8899);
  8. //2.获取一个输出流,用于输出数据
  9. os = socket.getOutputStream();
  10. //3.写出数据的操作
  11. os.write("你好,我是客户端mm".getBytes());
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. } finally {
  15. //4.资源的关闭
  16. if(os != null){
  17. try {
  18. os.close();
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. if(socket != null){
  24. try {
  25. socket.close();
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. }
  31. }
  32. public void server() {
  33. ServerSocket ss = null;
  34. Socket socket = null;
  35. InputStream is = null;
  36. ByteArrayOutputStream baos = null;
  37. try {
  38. //1.创建服务器端的ServerSocket,指明自己的端口号
  39. ss = new ServerSocket(8899);
  40. //2.调用accept()表示接收来自于客户端的socket
  41. socket = ss.accept();
  42. //3.获取输入流
  43. is = socket.getInputStream();
  44. //4.读取输入流中的数据
  45. baos = new ByteArrayOutputStream();
  46. byte[] buffer = new byte[5];
  47. int len;
  48. while((len = is.read(buffer)) != -1){
  49. baos.write(buffer,0,len);
  50. }
  51. System.out.println(baos.toString());
  52. System.out.println("收到了来自于:" + socket.getInetAddress().getHostAddress() + "的数据");
  53. } catch (IOException e) {
  54. e.printStackTrace();
  55. } finally {
  56. if(baos != null){
  57. //5.关闭资源
  58. try {
  59. baos.close();
  60. } catch (IOException e) {
  61. e.printStackTrace();
  62. }
  63. }
  64. if(is != null){
  65. try {
  66. is.close();
  67. } catch (IOException e) {
  68. e.printStackTrace();
  69. }
  70. }
  71. if(socket != null){
  72. try {
  73. socket.close();
  74. } catch (IOException e) {
  75. e.printStackTrace();
  76. }
  77. }
  78. if(ss != null){
  79. try {
  80. ss.close();
  81. } catch (IOException e) {
  82. e.printStackTrace();
  83. }
  84. }
  85. }
  86. }

image.png

UDP编程