java中的Tcp编程

Socket的解释

        在开发网络应用程序的时候,会遇到Socket这个概念。Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络。

        Socket、TCP和部分IP的功能都是由操作系统提供的,不同的编程语言只是提供了对操作系统调用的简单的封装。例如:Java提供的几个Socket相关的类就封装了操作系统提供的接口:ServerSocket类、Socket类。

        为什么需要Socket进行网络通信?因为仅仅通过IP地址进行通信是不够的,同一台计算机同一时间会运行多个网络应用程序,例如浏览器、QQ、邮件客户端等。当操作系统接收到一个数据包的时候,如果只有IP地址,它没法判断应该发给哪个应用程序,所以,操作系统抽象出Socket接口,每个应用程序需要各自对应到不同的Socket,数据包才能根据Socket正确地发到对应的应用程序。

        一个Socket就是由IP地址和端口号(范围是0~65535)组成,可以把Socket简单理解为IP地址+端口号。端口号总是由操作系统分配,它是一个0~65535之间的数字,其中,小于1024的端口属于特权端口,需要管理员权限,大于1024的端口可以由任意用户的应用程序打开。

        所以,如果需要与指定主机进行通信,完整的通信地址是由一个IP地址+端口号组成:

        ● 101.202.99.2:1201

        ● 101.202.99.2:1304

        ● 101.202.99.2:15000

        使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收数据。

因此,当Socket连接成功地在服务器端和客户端之间建立后:

        ●对服务器端来说:它的Socket是指定的IP地址和指定的端口号;

        ●对客户端来说:它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。

简易实现案例

 服务器端

public static void main(String[] args) {
    // 创建一个服务器套接字并监听在端口1923上
    // 使用try-with-resources确保ServerSocket在方法结束或发生异常时能够被正确关闭
    try(ServerSocket serverSocket = new ServerSocket(1923);){
        // 无限循环,服务器持续运行
        while(true) {
            // 阻塞等待直到一个客户端连接到来,然后创建一个Socket实例
            Socket clientSocket = serverSocket.accept();
            
            // 获取连接的客户端的IP地址
            InetAddress clientAddress = clientSocket.getInetAddress();
            
            // 打印客户端的IP地址,表明客户端正在连接并准备上传图片
            System.out.printf("客户端%s正在连接图片服务器,即将上传图片.....\n", clientAddress.getHostAddress());
            
            // 将客户端的IP地址转换为一个合法的文件名(替换点号为短划线)
            String img = clientAddress.getHostAddress().replace('.', '-').concat(".jpg");
            
            // 再次使用try-with-resources确保所有打开的流在方法结束或异常时能被正确关闭
            try(
                java.io.InputStream in = clientSocket.getInputStream(); // 从客户端socket获取输入流
                BufferedInputStream bis = new BufferedInputStream(in); // 包装输入流以提高读取效率
                BufferedOutputStream bos = new BufferedOutputStream( // 创建输出流到文件
                    new FileOutputStream("D:\\项目" + img)
                );
                BufferedWriter writer = new BufferedWriter( // 创建输出流到客户端,用于发送文本消息
                    new OutputStreamWriter(clientSocket.getOutputStream())
                );
            ){
                // 创建一个缓冲数组,用于从输入流中读取数据
                byte[] buff = new byte[1024];
                
                // 初始化读取长度,-1表示尚未读取数据
                int len = -1;
                
                // 循环读取客户端上传的数据直到读取完毕(注意:这里的终止条件应是len == -1,而不是1)
                while((len = bis.read(buff)) != -1) {
                    // 将读取到的数据写入到文件中
                    bos.write(buff, 0, len);
                }
                
                // 向客户端发送上传成功的确认消息
                writer.write("upload success!!!");
            }
        }
    }
    // 捕获并处理可能发生的IO异常
    catch (IOException e) {
        e.printStackTrace();
    }
}

 客户端

public static void main(String[] args) {
    // 这行代码被注释掉了,原本是用来创建一个File对象的,但在此示例中未被使用。
    // File f = new File("D:\\项目\\1.jpg");

    // 尝试建立一个Socket连接到指定的服务器IP地址和端口号
    try (Socket socket = new Socket("192.168.199.157", 8848);
         // 创建一个BufferedInputStream用于从本地文件读取数据
         BufferedInputStream in = new BufferedInputStream(new FileInputStream("D:\\项目\\1.jpg"));
         // 创建一个BufferedOutputStream用于向Socket的输出流写入数据
         BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
         // 创建一个BufferedReader用于从Socket的输入流读取文本数据
         BufferedReader red = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {

        // 创建一个缓冲区用于存储从文件读取的数据
        byte[] buff = new byte[1024];

        // 初始化读取长度,-1表示尚未读取数据
        int len = -1;

        // 循环读取本地文件数据,直到文件结束(read方法返回-1)
        while ((len = in.read(buff)) != -1) {
            // 将从文件读取的数据写入到Socket的输出流中
            out.write(buff, 0, len);
        }

        // 刷新输出流,确保所有数据都被发送出去
        out.flush();

        // 关闭Socket的输出流,这通常用于通知接收方数据发送完毕
        socket.shutdownOutput();

        // 读取服务器通过Socket发送回来的响应,直到没有更多数据可读(readLine返回null)
        String line = null;
        while ((line = red.readLine()) != null) {
            // 打印服务器返回的每一行文本
            System.out.println(line);
        }
    } 
    // 捕获并处理可能发生的IOException异常
    catch (IOException e) {
        // 打印异常堆栈跟踪信息
        e.printStackTrace();
    }
}

小结

使用Java进行TCP编程时,需要使用Socket模型:

        ●服务器端用ServerSocket监听指定端口;

        ●客户端使用Socket(InetAddress, port)连接服务器;

        ●服务器端用accept()接收连接并返回Socket实例;

        ●双方通过Socket打开InputStream/OutputStream读写数据;

        ●服务器端通常使用多线程同时处理多个客户端连接,利用线程池可大幅提升效率;

        ●flush()方法用于强制输出缓冲区到网络。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值