网络编程(总)

本文详细介绍了网络通信的基础知识,包括IP地址、域名、端口和网络协议。深入探讨了TCP与UDP协议的区别,TCP的三次握手确保可靠性,而UDP则注重效率。通过Java API展示了Socket的使用,并提供了TCP和UDP的网络编程案例,包括文件传输。还提到了网络调试工具netstat的使用,以及在编程中需要注意的细节和优化技巧。
摘要由CSDN通过智能技术生成

TCP/UDP网络编程知识总结

网络通信基础知识

概念:两台或多台设备进行数据的传输。

网络

概念:两台或多台设备通过一定物理设备连接来构成网络。
网络的分类:

- 局域网:(范围逐渐变大),一个小型区域
- 城域网:一个城市
- 广域网:一个国家(典型代表是局域网)

网络编程的目的

网络编程的目的:直接或间接地通过网络协议与其他计算机实现数据交换进行通讯
要想实现网络编程,需要关注两个问题:

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

IP地址

  1. 概念:唯一标识主机的。
  2. 查看ip地址:ipconfig
  3. 在代码中查看本机ip :InetAddress.getLocalHost()
    主机地址查看
  4. IP地址表示:4个字节(32位)表示,一个字节的范围是0~255;
  5. IP地址的格式:网络地址+主机地址。
  6. IPV6地址(解决了IPV4在网络地址上的资源有限问题),IPV6可以实现多种接入设备连入互联网。 IPV6是由128位(128/16 = 8字节)表示一个地址。

域名

  1. www.baidu.com着就是百度的域名,他可以理解成是IP地址的映射(因为ip地址不容易记忆,所以就有域名的出现

端口

概念:用于标识计算机上某个特定的网路程序。
端口号:范围0~65535.
电脑浏览器如果想访问一个网站需要通过(IP+端口号)
访问
在网络开发中最好不要使用0~1024号端口,因为这些端口可能已经被其他服务器使用了
常见端口号

  • 一个服务要接收和发送数据的话,需要有一个端口。端口就类似于人的耳朵,我们要听懂别人的话,必须要有耳朵

网络通信协议

在这里插入图片描述

TCP协议于UDP协议的区别

  • TCP类似于打电话(稳定性传输)
  • UDP类似于发短信(不可靠的)
TCP 协议:传输控制协议

就是确定是否连接成功,需要进行三次确认。

  • TCP协议的优点:需要进行三次握手(是可靠的)
  • 缺点就是效率低。
    简单理解TCP协议
UDP协议:用户数据协议
  • UDP协议无法保证传输的数据一定被接收到
  • UDP协议就是没有确认对方是否能够接收到消息,就直接传输信息
  • 比如说:你发送信息给别人,但是这个电话号码可能停机了或者说注销了,你不能确保别人能接收消息,类似于UDP协议
  • 在这里插入图片描述
    假如说现在有一个人也想要打电话给kim,由于kim这个时候在和tom打电话,他们两个如果电话没有挂掉,这个人的电话是打不通的,就类似于TCP协议要释放已经建立的连接

tom发信息给kim,另外一个人同样也可以发信息给kim,就类似于UDP协议不需要释放资源
在这里插入图片描述

简单的API使用

InetAddress

一个InetAddress类的对象就当于是一个IP地址

相关方法

getByName(String host) 、 getLocalHost()
两个常用方法:getHostName() 获取域名 / getHostAddress() 获取主机地址

实例

例子

代码
package 网络编程;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class API {

    public static void main(String[] args) throws UnknownHostException {
        //获取本机的IntetAddress的对象
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);

        //根据指定主机名,获取InetAddress对象
        InetAddress host1 = InetAddress.getByName("LAPTOP-612T08NP");
        System.out.println(host1);

        //根据域名返回InetAddress对象,比如百度的域名
        InetAddress host2 = InetAddress.getByName("www.baidu.com");
        System.out.println(host2);

        //根据域名获取Ip
        String hostAddress = host2.getHostAddress();
        System.out.println("host2 对应的IP = "+ hostAddress);
    }
}

常用的方法就是上面四种,分别来获取IP和对应的域名。

Socket理解

- Socket就类似于通信的通道,发送数据是通过socket
- TCP编程:可靠的,但是效率低
- UDP编程:不可靠的,但是效率高

简单理解就是数据两端的插头,让客户端与服务器端可以很好的取得连接。目的就是为了通信,而通信的目的就是用来发送数据和接收数据。

socket传输协议

TCP网络通信案例

案例一:

使用字节流。编写一个服务器端和一个客户端,服务器端在9999端口进行监听,客户端连接到服务器端发送一个“hello world”然后退出,服务器端接收到客户端的信息输出并退出。
思路分析

服务端:


package 网络编程;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketTCPServer {
    public static void main(String[] args) throws IOException {
        //在本机9999端口接听,等待连接
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器,在9999端口监听,等待连接。。。");
        //当没有客户端连接999端口时,程序会阻塞,等待连接
        //如果有客户端连接。则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        System.out.println("socket = "+socket.getClass());
    }
}

在这里插入图片描述
此时9999端口已经占用了,如果再次点击运行,程序会报错。在这里插入图片描述

案例二:TCP编写(字节读写)

案例要求:

案例要求

客户端:
- 连接服务端(IP,端口)
- 连接上后,生成Socket,通过socket.getOutputStream()函数创建输出流
- 通过输出流,写入数据到数据通道(服务器端来接收)
package 网络编程;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class SocketTCPClient {
    public static void main(String[] args) throws IOException {
        //连接TCP端(ip,端口)
        //连接本机9999端口,如果连接成功,返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);
        System.out.println("客户端 socked返回"+socket.getClass());
        //连接成功后,生成socket对象,通过socket.getOutputStream()(输出流)输出hello server
        //得到和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //通过输出流,写入数据到数据通道
        outputStream.write("hello server".getBytes());
        //关闭流对象的socket,
        outputStream.close();
        socket.close();
        System.out.println("客户端退出!");




    }
}

服务端
- 在本机的9999 端口监听,等待连接
- 当没有客户端连接9999 端口时,程序会阻塞,等待连接
- 通过socket.getInputStream();函数来读取客户端写入到数据通道的数据,然后显示出来。
package 网络编程;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketTCPServer {
    public static void main(String[] args) throws IOException {
        //在本机9999端口接听,等待连接
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器,在9999端口监听,等待连接。。。");
        //当没有客户端连接999端口时,程序会阻塞,等待连接
        //如果有客户端连接。则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        System.out.println("服务器 socket = "+socket.getClass());
        //通过输入流来读取客户端传来的数据
        InputStream inputStream = socket.getInputStream();
        //IO读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1){
            System.out.println(new String(buf,0,readLen));//根据
        }
        //关闭流
        inputStream.close();
        socket.close();
        serverSocket.close();
        
    }
}


注意事项
  • 运行程序的时候一定是先运行server段,再运行client客户端

案例三:

思路解析
服务端
新添加两行代码

package 网络编程;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketTCPServer {
    public static void main(String[] args) throws IOException {
        //在本机9999端口接听,等待连接
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器,在9999端口监听,等待连接。。。");
        //当没有客户端连接999端口时,程序会阻塞,等待连接
        //如果有客户端连接。则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        System.out.println("服务器 socket = "+socket.getClass());
        //通过输入流来读取客户端传来的数据
        InputStream inputStream = socket.getInputStream();
        //IO读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1){
            System.out.println(new String(buf,0,readLen));//根据
        }

        //获取socket相关联的输出流
        OutputStream outputStream =  socket.getOutputStream();
        //写入数据到数据通道
        outputStream.write("hello,client".getBytes());
        //关闭流
        inputStream.close();
        socket.close();
        serverSocket.close();
        //关闭输出流
        outputStream.close();
        
    }
}


在这里插入图片描述

  • server端有输出,但是没有退出,好像是卡到了什么地方。clint并没有接收到server端发送的数据。
  • 如何解决上面的问题,就是给两个端设置结束标志。socket。shutdownOutput();关闭了输出的结束。

server端
clint端

代码优化

服务端:

package 网络编程;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketTCPServer {
    public static void main(String[] args) throws IOException {
        //在本机9999端口接听,等待连接
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器,在9999端口监听,等待连接。。。");
        //当没有客户端连接999端口时,程序会阻塞,等待连接
        //如果有客户端连接。则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        System.out.println("服务器 socket = "+socket.getClass());
        //通过输入流来读取客户端传来的数据
        InputStream inputStream = socket.getInputStream();
        //IO读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1){
            System.out.println(new String(buf,0,readLen));//根据
        }

        //获取socket相关联的输出流
        OutputStream outputStream =  socket.getOutputStream();
        //写入数据到数据通道
        outputStream.write("hello,client".getBytes());
        //设置结束标志
        socket.shutdownOutput();
        //关闭流
        inputStream.close();
        socket.close();
        serverSocket.close();
        //关闭输出流
        outputStream.close();
        
    }
}

客户端:

package 网络编程;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class SocketTCPClient {
    public static void main(String[] args) throws IOException {
        //连接TCP端(ip,端口)
        //连接本机9999端口,如果连接成功,返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);
        System.out.println("客户端 socked返回"+socket.getClass());
        //连接成功后,生成socket对象,通过socket.getOutputStream()(输出流)输出hello server
        //得到和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //通过输出流,写入数据到数据通道
        outputStream.write("hello server".getBytes());
        //设置结束标志
        socket.shutdownOutput();
        //获取和socket关联的输入流,读取数据(字节),并显示
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1){
            System.out.println(new String(buf,0,readLen));//根据
        }
        //关闭流对象的socket,
        outputStream.close();
        socket.close();
        System.out.println("客户端退出!");
        //关闭输入流
        inputStream.close();




    }
}

案例四:文件的传输

TCP网络通信编程
分析:将客户端(Client)的图片上传到服务器(server),本质就是一个图片上传。同时服务器要恢复消息。

上传逻辑
  • 先将磁盘的图片通过文件输入流上传到程序中,程序中使用字节数组来保存。然后程序中使用socket将其传输到服务端。
  • 传输流程图
代码

代码都写了详细的注释

  • stramutils工具类,主要是讲文件的二进制码转换成字节,存到数组里面。
package upload;

/**
 * 工具类的作用:
 * 处理各种情况的用户输入,并且能按照程序员对的需求,得到用户的控制台输入
 */

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Scanner;
import java.util.*;
public class StreamUtils {
    //静态属性
    private static Scanner scanner = new Scanner(System.in);

    public static byte[] streamToByteArray(InputStream is) throws Exception{
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
        byte[] b = new byte[1024];//字节数组
        int len;
        while((len=is.read(b))!=-1){//循环读取
            bos.write(b,0, len);//把读取到的数据,写入bos
        }
        byte[] array = bos.toByteArray();//然后将bos转成字节数组bos.close();
        return array;
    }
    /**
     * 功能:读取键盘输入的一个菜单项,值:1-5范围
     */
    public static char readMenuSelection() {
        char c;
        for (; ; ) {
            String str = readKeyBoard(1, false);
            c = str.charAt(0);//将字符串转换为字符
            if (c != '1' && c != '2' && c != '3' && c != '4' && c != '5') {
                System.out.println("选择错误,请重新输入:");
            } else break;
        }
        return c;
    }

    /**
     * 功能:读取键盘输入的一个字符
     */
    public static char readChar() {
        String str = readKeyBoard(1, false);//就是一个字符
        return str.charAt(0);
    }

    /**
     * 功能;读取键盘输入的一个字符,如果直接回车,则返回指定的默认值
     * defaultValue 指定的默认值
     * return 默认值或者输入的字符
     */
    public static char readChar(char defaultValue) {
        String str = readKeyBoard(1, true);//要么是空字符,要么??
        return str.length() == 0 ? defaultValue : str.charAt(0);
    }


    /**
     * 读取键盘输入的整型,长度少于两位
     * return 整数
     */
    public static int readInt() {
        int n;
        for (; ; ) {
            String str = readKeyBoard(2, false);
            try {
                n = Integer.parseInt(str);
                break;
            } catch (NumberFormatException e) {
                System.out.println("选择错误,请重新输入:");
            }
        }
        return n;
    }

    /**
     * 功能:读取键盘输入的整数或者默认值,如果直接回车,则返回默认值
     * defaultValue 指定的默认值
     * return 整数或者默认值
     */
    public static int readInt(int defaultValue) {
        int n;
        for (; ; ) {
            String str = readKeyBoard(2, false);
            if (str.equals("")) {
                return defaultValue;
            }

            try {
                n = Integer.parseInt(str);
                break;
            } catch (NumberFormatException e) {
                System.out.println("选择错误,请重新输入:");
            }
        }
        return n;
    }

    /**
     * 功能:读取键盘输入的指定长度的字符串
     * limit:限制的长度
     * return:指定长度的字符串
     */
    public static String readString(int limit) {
        return readKeyBoard(limit, false);
    }

    /**
     * 功能:读取键盘输入的指定长度的字符串或者默认值,如果直接回车,返回默认值
     * limit:限制的长度
     * defaultValue 指定的默认值
     * return 指定长度的字符串
     */
    public static String readString(int limit, String defaultValue) {
        String str = readKeyBoard(limit, true);
        return str.equals("") ? defaultValue : str;
    }

    /**
     * 功能:读取键盘输入的确认选项,Y或N
     * 将小的功能,封装到一个方法中
     * return Y/N
     */
    public static char readConfirmSelection() {
        System.out.println("请输入你的选择(Y/N)");
        char c;
        for (; ; ) {//无限循环
            //在这里,将接收到字符,转换程大写字母
            //y=>Y,n=>N
            String str = readKeyBoard(1, false).toUpperCase();
            c = str.charAt(0);
            if (c == 'Y' || c == 'N') {
                break;
            } else {
                System.out.print("选择错误,请重新输入:");
            }
        }
        return c;
    }

    /**
     * 功能:读取键盘输入,如果输入为空,或者输入大于limit的长度,就会提示重新输入
     */
    private static String readKeyBoard(int limit, boolean blankReturn){
        //定义了字符串
        String line="";

        //scanner.hasNextLine()判断有没有下一行
        while (scanner.hasNextLine()){
            line=scanner.nextLine();//读取下一行

            //如果Line.length=0,即用户没有输入任何内容,直接回车
            if(line.length()==0){
                if(blankReturn) return line;
                else continue;//如果blackRuturn=false,不接受空串,必须输入内容
            }

            //如果用户输入的内容大于了limit,就提示重写输入
            //如果用户输入的内容哦个>0<=limit,我就接受
            if(line.length()<1 || line.length()>limit){
                System.out.println("输入长度(不大于 "+ limit + ")错误,请重新输入:");
                continue;
            }
            break;
        }
        return line;
    }

}
  • server端
package upload;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPFileUploadServer {
    public static void main(String[] args) throws Exception {
        //服务端在本机监听8888端口
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在8888端口监听");
        //等待客户端连接,如果有客户端连接,就会有一个socke;
        Socket socket = serverSocket.accept();

        //读取客户端发送的数据
        //通过socket得到一个输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        //读取
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        //完成将得到的byte数组,写入到指定的路径,就得到一个文件了
        String destFilePath = "D:\\2111.png";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        bos.write(bytes);//把字节数组写进去
        bos.close();

        //关闭其他资源
        bis.close();
        socket.close();
        serverSocket.close();


    }
}

client端:

package upload;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class TCPFiledUploadClient {
    public static void main(String[] args) throws Exception {
        //客户端代码
        //客户端连接服务端,得到socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(),8888);
        //创建读取磁盘文件的输入流
        String filePath = "D:\\111.png";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        //bytes 就是filepath对应的字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bis);

        //通过socket获取输出流,将bit数据发送给服务端
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
        //这里要设置一个结束标志
        bis.close();//关闭输入流
        socket.shutdownOutput();//设置写入数据的结束标记。这个很关键

        //关闭相关的流
        bos.close();
        socket.close();
    }


}

注意事项

结束标志

效果

实现了文件的复制

优化

图片复制以后,会给客户端返回一句话“收到图片”;
server端优化
客户端接收并打印
client端优化

netstat指令

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

  • Listening 表示某个端口在监听。
  • 如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息。
  • ctrl+c退出程序

UDP网络通信

基本流程

原理图

  • 核心的两个类:DatagramSocket与DatagramPacket
  • 建立发送端和接收端(没有服务端和客户端这一说法)
  • 发送数据前,建立数据包DatagramPacket对象
  • 调用DatagramSocket的发送,DatagramPacket对象
  • 关闭DatagramSocket

UDP网络编程案例

  • 接收端A接收数据的代码
    在这里插入图片描述
package UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UDPReceiverA {
    public static void main(String[] args) throws IOException {
        //创建一个DatagramSocket对象,准备在9999的端口接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        //构建一个DatagramPacket对象,准备接收数据
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        //调用接收方法,
        //当有数据包发送到本机的9999端口时,就会接收到数据,如果没有数据发送到9999端口,就会阻塞
        System.out.println("接收端A 等待接收数据");
        socket.receive(packet);

        //可以把数据packet进行拆包,取出数据并显示
        int length = packet.getLength();//实际接收到的数据长度
        byte[] data = packet.getData();//接收到的数据
        String s = new String (data,0,length);
        System.out.println(s);

        //关闭资源
        socket.close();
    }
}

注意事项
  • DatagramSocket既可以接收数据也可以发送数据

发送数据端B发送代码:
发送端

package UDP;

import java.io.IOException;
import java.net.*;

public class UDPSenderB {
    public static void main(String[] args) throws IOException {
        //创建DatagramSocket对象,准备发送和接收数据
        DatagramSocket socket = new DatagramSocket(9998);

        //将要发送的数据封装到DatagramPacket对象
        byte[] data = "hello  渣渣鑫".getBytes();
        //DatagramPacket(字节数组(要发送的内容),长度,主机ip,端口)
       DatagramPacket packet = new DatagramPacket(data,data.length, InetAddress.getByName("192.168.56.1"),9999);
        //发送
        socket.send(packet);

        socket.close();

    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仰望星空的鑫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值