Java通信——传文件、消息

  1. 服务器Server端
    1. 接收普通消息
    2. 接收文件
  2. 客户机client端
    1. 发送普通消息
    2. 发送文件
  3. 代码
    1. server
    2. client

服务器Server端

首先,创建服务器,循环等待接入客户端

java.net.ServerSocket server = new java.net.ServerSocket(port);//创建服务器
java.net.Socket client = server.accept();//客户端接入

接入客户端之后,开始接受消息。

在这里,接受的消息不只是一个简单的内容,还含有消息头,消息头中含有有关消息的规范。

  1. 消息长度——一个int(4字节)
  2. 消息类型——一个byte(1字节)
  3. 目标用户号码——一个int(4字节)

消息类型这里分为两种:

  • 1代表普通消息
  • 2代表文件

接收普通消息

byte[] data = new byte[totalLen-4-1-4];
//从流中读取data.length个字节放入数组
dins.readFully(data);
String msg = new String(data);
System.out.println("发送文本给:"+destNum+"内容是:"+msg);

这里注意readfully和read的区别:

网络通信中,当发送大的数据量时,有这样一种可能:一部
分数据已发送到对方,有一部分数据还在本地的网卡缓存中,如果调用 read()方法,可能会提前
返回而没有读取到足够的数据,在传送大块数据(如一次传送一个较大文件时)可能会出错,而
readFully()方法会一直等待,读取到数组长度的所有数据后,才会返回。

这里是个大坑,我之前传图片会出现传送之后,接受到的图片只显示一半。原因就在此。

我用read进行了测试,结果生成的图片虽然大小一模一样,但是生成图片无法显示!!!

接收文件

//256个字节作为文件名大小
System.out.println("发送文件给:"+destNum);    //destnum为接收者
byte[] data = new byte[256];
dins.readFully(data);
String fileName = new String (data).trim();
System.out.println("读到文件名字是:"+fileName);
//读文件内容
data = new byte[totalLen-4-1-4-256];
dins.readFully(data);
//保存在当前目录下
FileOutputStream fous = new FileOutputStream(fileName);
fous.write(data);
fous.flush();    //保证可靠
fous.close();

关于flush:原文链接:https://blog.csdn.net/qq_38129062/article/details/87115620

设想要给鱼缸换水,所以需要一个水泵,水泵是连接鱼缸和下水道的,咱们的任务就是将鱼缸里面水全抽干,这时,我们就可以把水管当做缓冲区。如果咱们一见鱼缸里面水抽干了就立马关了水泵,这时会发现水管里还有来不及通过水泵流向下水道的残留水,我们可以把抽水当做读数据,排水当做写数据,水管当做缓冲区,这样就容易明白了。

那么这样一来我们如果中途调用close()方法,输出区也还是有数据的,就像水缸里有水,只是在缓冲区遗留了一部分,这时如果我们先调用flush()方法,就会强制把数据输出,缓存区就清空了,最后再关闭读写流调用close()就完成了。

收文件时,用了FileOutputStream,文件输出流,它把文件直接写在了当前目录下。
可以直接规定文件输出的位置:

FileOutputStream fous = new FileOutputStream("C:\\Users\\董润泽\\Desktop");

客户机client端

首先创建客户端并通过ip和主机端口连接到服务器

java.net.Socket client = new java.net.Socket(ip,port);

然后获取输入流和输出流:

java.io.InputStream ins = client.getInputStream();
OutputStream ous = client.getOutputStream();

在while循环内持续发送信息

发送普通消息

byte[] strb = msg.getBytes();//得到消息的字节数
int totalLen = 4+4+1+strb.length;
System.out.println("发送总长为:"+totalLen);
dous.writeInt(totalLen);
dous.writeByte(1);//类型1代表文本
dous.writeInt(destNum);//写入目标用户号
dous.write(strb);
dous.flush();

其中dous为数据输出流

private DataOutputStream dous;

发送文件

//根据文件名创建文件对象
File file = new File(fileName);
//根据文件对象构造一个输入流
FileInputStream ins = new FileInputStream(file);
int fileDataLen = ins.available(); //文件数据总长
int totalLen = 4+1+4+256+fileDataLen; //得到了要发送数据包的总长
dous.writeInt(totalLen);
dous.writeByte(2);
dous.writeInt(destNum);
String shortFileName = file.getName();
//写入文件名
writeString(dous,shortFileName,256);
byte[] fileData = new byte[fileDataLen];
ins.read(fileData);//读入文件数据
dous.write(fileData);//写到服务器的流中
dous.flush();

注意:发送文本和文件的正文前,首先要发出文件头!

代码

server

package qq_Server_test;

import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;

import org.omg.CORBA.portable.InputStream;

public class Server {
    public void setUpServer(int port) {
        try {
            java.net.ServerSocket server = new java.net.ServerSocket(port);
            System.out.println("服务器创建完成");
            while (true) {
                java.net.Socket client = server.accept();
                System.out.println("客户端已接入");
                process(client);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void process(java.net.Socket client) {
        try {
            OutputStream ous = client.getOutputStream();
            java.io.InputStream ins = client.getInputStream();
            //将输入流包装成DataInputStream以便读取数据
            DataInputStream dins = new DataInputStream(ins);
            while(true){
                //读取消息,消息以int开头
                //1、读消息长度
                int totalLen = dins.readInt();
                System.out.println("发来消息长度为:"+totalLen);
                //2、读消息类型标识,读一个字节
                byte flag = dins.readByte();
                System.out.println("接收消息类型为:"+flag);
                //3、读取目标客户号码,一个 int
                int destNum = dins.readInt();
                System.out.println("目标用户号码为:"+destNum);
                //根据消息内容读取消息体
                if(flag==1){//文本消息
                    byte[] data = new byte[totalLen-4-1-4];
                    //从流中读取data.length个字节放入数组
                    dins.readFully(data);
                    String msg = new String(data);
                    System.out.println("发送文本给:"+destNum+"内容是:"+msg);
                }
                else if(flag==2){//文件数据
                    //256个字节作为文件名
                    System.out.println("发送文件给:"+destNum);
                    byte[] data = new byte[256];
                    dins.readFully(data);
                    String fileName = new String (data).trim();
                    System.out.println("读到文件名字是:"+fileName);
                    //读文件内容
                    data = new byte[totalLen-4-1-4-256];
                    dins.readFully(data);
                    //保存在当前目录下
                    FileOutputStream fous = new FileOutputStream(fileName+1);
                    fous.write(data);
                    fous.flush();
                    fous.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Server server = new Server();
        server.setUpServer(9900);
    }
}

client

package qq_client_test;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

import org.omg.CORBA.portable.InputStream;

public class Client {
    private DataOutputStream dous;//输出流对象

    private void writeString(DataOutputStream out,String str,int len){
    //向流中写入指定长度的字节,如果不足,就补0
        try{
            byte[] data = str.getBytes();
            out.write(data);
            while(len>data.length){
                out.writeByte('\0');
                len--;
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private void sendTextMsg(String msg,int destNum){
        //发送消息
        try{
            byte[] strb = msg.getBytes();//得到消息的字节数
            int totalLen = 4+4+1+strb.length;
            System.out.println("发送总长为:"+totalLen);
            dous.writeInt(totalLen);
            dous.writeByte(1);//类型1代表文本
            dous.writeInt(destNum);//写入目标用户号
            dous.write(strb);
            dous.flush();            
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private void sendFileMsg(String fileName, int destNum){
        try{
            //根据文件名创建文件对象
            File file = new File(fileName);
            //根据文件对象构造一个输入流
            FileInputStream ins = new FileInputStream(file);
            int fileDataLen = ins.available(); //文件数据总长
            int totalLen = 4+1+4+256+fileDataLen; //得到了要发送数据包的总长
            dous.writeInt(totalLen);
            dous.writeByte(2);
            dous.writeInt(destNum);
            String shortFileName = file.getName();
            //写入文件名
            writeString(dous,shortFileName,256);
            byte[] fileData = new byte[fileDataLen];
            ins.read(fileData);//读入文件数据
            dous.write(fileData);//写到服务器的流中
            dous.flush();

        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public void conn2Server(String ip, int port){
        try{
            java.net.Socket client = new java.net.Socket(ip,port);
            java.io.InputStream ins = client.getInputStream();
            OutputStream ous = client.getOutputStream();
            dous = new DataOutputStream(ous);
            int testCount = 0;
            while(true){
                System.out.println("登陆服务器成功,选择要发的类型(1、聊天,2、文件):");
                java.util.Scanner sc = new Scanner(System.in);
                int type = sc.nextInt();
                if(type==1){
                    sendTextMsg("abc聊天"+testCount, 8888);
                }
                if(type==2){
                    sendFileMsg("C:\\Users\\董润泽\\Desktop\\test1.jpg", 8888);
                }
                testCount++;
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Client client = new Client();
        client.conn2Server("localhost", 9900);
    }
}

运行结果:



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 2470290795@qq.com

文章标题:Java通信——传文件、消息

文章字数:1.9k

本文作者:runze

发布时间:2020-01-29, 19:47:57

最后更新:2020-01-29, 20:42:05

原始链接:http://yoursite.com/2020/01/29/Java/Java%E9%80%9A%E4%BF%A1%E2%80%94%E2%80%94%E4%BC%A0%E6%96%87%E4%BB%B6/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏