java网络编程 1 (socket通信、文件上传、模拟BS服务器)

1、写在前面

软件结构:

  • CS 结构:客户端-服务器结构
  • BS 结构:浏览器-服务器结构

网络通信协议:

  • TCP/IP协议:因特网连接协议

tcp/ip协议分为4层,链路层、传输层、网络层和应用层。每一层完成不同的功能,共同作用完成网络传输服务。传输层协议有TCP、UDP等。

  • UDP: 面向无连接的通信协议,例如视频会议、qq发送文字。大小被限制在64k以内。
  • TCP: 面向连接的通信协议,每次连接需要经过三次握手,例如下载文件、网页浏览。

ip协议:

  • ip 编址:ipv4是一个32位的二进制数,常用a.b.c.d这样的四个字节表示。
  • ip 协议:是TCP/IP体系中的网络层协议。对上可载送传输层各种协议的信息,对下可将IP信息包放到链路层。ip地址就是ip协议中的一部分。
  • 相关指令:ipconfig 、ping

端口号:

  • 端口号:一台计算机对应一个ip地址,一台计算机有多个端口号对应多个服务或应用。
  • 常用端口号:80是HTTP默认端口号、22是scp/ssh的tcp端口号
  • 指令:“netstat -n”显示地址和端口信息

java TCP通信:

  • 通信方式:通过Socket套接字传输数据,套接字的输入流用InputStream接收、输出流用OutputStream接收。
  • Scoket类参数:String host 主机名称或ip地址、int port端口号。
  • 成员方法:close关闭套接字

2、使用soket套接字

2.1、使用soket套接字通信

客户端

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

public class TCPClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",7890);
        OutputStream o = socket.getOutputStream();                     // 获取输出流
        o.write("你好,服务器".getBytes());
        socket.close();
    }
}

服务端

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

/*
 * 服务端
 * 客户端发送消息过来,服务端获取到客户端对象、消息,通过输入流将消息接收到
 * 先运行服务端,再运行客户端就可以接收到消息:你好,服务器
 **/
public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(7890);
        Socket socket = server.accept();                      // 获取到客户端对象
        InputStream is = socket.getInputStream();             // 输入流对象
        byte[] bytes = new byte[1024];
        int l = is.read(bytes);                               // 返回字节的实际长度
        System.out.println(new String(bytes,0,l));

        OutputStream os = socket.getOutputStream();
        os.write("收到".getBytes());

        socket.close();
        server.close();
    }
}

2.2、文件上传

客户端

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

// 客户端
public class TCPClient {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("d:\\1.png");
        Socket socket = new Socket("127.0.0.1",8888);
        OutputStream os = socket.getOutputStream();

        // 读取本地文件
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = fis.read(bytes))!=-1){
            os.write(bytes,0,len);
        }

        // fis.read(bytes)是读取本地文件,结束标记是读取到-1
        // socket.shutdownOutput(); 就是结束标记
        socket.shutdownOutput();


        // 读取服务器回写数据
        InputStream is = socket.getInputStream();
        while ((len = is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

        fis.close();
        socket.close();
    }
}

服务端

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

// 服务器
public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        Socket socket = server.accept();
        // 获取输入流
        InputStream is = socket.getInputStream();

        // 判断文件夹是否存在
        File file = new File("d:\\upload");
        if(!file.exists()){
            file.mkdirs();
        }
        // 将文件数据保存到磁盘上
        FileOutputStream fos = new FileOutputStream(file+"\\1.jpg");
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = is.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        socket.getOutputStream().write("上传成功".getBytes());
        fos.close();
        socket.close();
        server.close();
    }
}
2.3.1、防止同名文件覆盖

在服务器端,把文件名改成这个

        // 自定义一个命名规则,防止同名文件覆盖
        String fileName = "itcast"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

在这里插入图片描述

2.3.2、让服务器一直处于监听状态

在这里插入图片描述

2.3.3、利用多线程,提高效率
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;

// 服务器
public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        while (true){
            Socket socket = server.accept();
            // 有一个客户端上传文件,就开启一个线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 获取输入流
                        InputStream is = socket.getInputStream();

                        // 判断文件夹是否存在
                        File file = new File("d:\\upload");
                        if(!file.exists()){
                            file.mkdirs();
                        }

                        // 自定义一个命名规则,防止同名文件覆盖
                        String fileName = "itcast"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

                        // 将文件数据保存到磁盘上
                        FileOutputStream fos = new FileOutputStream(file+"\\"+fileName);
                        int len = 0;
                        byte[] bytes = new byte[1024];
                        while ((len = is.read(bytes))!=-1){
                            fos.write(bytes,0,len);
                        }
                        socket.getOutputStream().write("上传成功".getBytes());
                        fos.close();
                        socket.close();
                    }catch (IOException e){
                        System.out.println(e);
                    }
                }
            }).start();


        }
    }
}

在这里插入图片描述

2.3、模拟BS服务器

先创建一个简单的服务器

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

// BS版本的TCP服务器
public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8080);
        Socket socket = server.accept();
        // 获取输入流
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len=is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

    }
}

把一个html文件放进项目目录下
在这里插入图片描述
启动这个服务器,用浏览器访问这个html
在这里插入图片描述
在这里插入图片描述

查看服务器后台,我们可以看到socket getInputStream收到的内容
在这里插入图片描述
接下来我们提取getInputStream收到的内容的第一排:
GET /java_socket/src/socket03/baidu.html HTTP/1.1

并获取对应的路径,把对应的资源写入到输出流

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

// BS版本的TCP服务器
public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8080);
        Socket socket = server.accept();
        // 获取输入流
        InputStream is = socket.getInputStream();
        // 网络输入流转为字符串缓冲流
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        // 读取客户端请求信息的第一行
        String line = br.readLine();
        // 利用空格切割出地址
        String[] arr = line.split(" ");
        // 把路径最前面的"/"去掉
        String path = arr[1].substring(1);
        System.out.println(path);
        // 读取对应路径下的文件
        FileInputStream fileInputStream = new FileInputStream(path);
        // 写入客户端
        OutputStream os = socket.getOutputStream();
        // 写入http相应头(固定写法)
        os.write("HTTP/1.1 200 OK\r\n".getBytes());
        os.write("Content-Type:text/html\r\n".getBytes());
        os.write("\r\n".getBytes());

        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = fileInputStream.read(bytes))!=-1){
            os.write(bytes,0,len);
        }

        fileInputStream.close();
        socket.close();
        server.close();
    }
}

启动这个main方法,现在通过浏览器就能访问到对应的html了
在这里插入图片描述
如果我们这个html里面有图片等,我们会发现显示不出来,这是因为页面中有图片,那么浏览器会单独开启一个线程读取图片,所以我们需要写一个多线程循环,让服务器处于监听状态,客户端请求一次,服务器就回写一次。

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

// BS版本的TCP服务器
public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8080);
        while (true){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        Socket socket = server.accept();
                        // 获取输入流
                        InputStream                        is = socket.getInputStream();

                        // 网络输入流转为字符串缓冲流
                        BufferedReader br = new BufferedReader(new InputStreamReader(is));
                        // 读取客户端请求信息的第一行
                        String line = br.readLine();
                        // 利用空格切割出地址
                        String[] arr = line.split(" ");
                        // 把路径最前面的"/"去掉
                        String path = arr[1].substring(1);
                        System.out.println(path);
                        // 读取对应路径下的文件
                        FileInputStream fileInputStream = new FileInputStream(path);
                        // 写入客户端
                        OutputStream os = socket.getOutputStream();
                        // 写入http相应头(固定写法)
                        os.write("HTTP/1.1 200 OK\r\n".getBytes());
                        os.write("Content-Type:text/html\r\n".getBytes());
                        os.write("\r\n".getBytes());

                        int len = 0;
                        byte[] bytes = new byte[1024];
                        while ((len = fileInputStream.read(bytes))!=-1){
                            os.write(bytes,0,len);
                        }

                        fileInputStream.close();
                        socket.close();
                    }catch (Exception e){
                        System.out.println(e);
                    };
                }
            });
        }

    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值