day21Java-常用对象网络编程Socket-TCP协议

博客
Java-(高级)

TCP协议

java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。
java.net.ServerSocket:此类实现服务器套接字。

TCP:
		建立连接通道
		数据无限制
		速度慢
		可靠

连接被拒绝。TCP协议一定要先看服务器。
java.net.ConnectException: Connection refused: connect

TCP协议发送和接收数据图解
在这里插入图片描述

TCP协议编程演示
TCP协议发送数据:
A:创建发送端的Socket对象
这一步如果成功,就说明连接已经建立成功了。
B:获取输出流,写数据
C:释放资源

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP客户端对象
        //Socket(InetAddress address, int port):使用InetAddress指定ip和指定端口。
        //Socket(String host, int port):指定ip和端口
        //Socket s = new Socket(InetAddress.getByName("192.168.150.1"),10003);
        Socket s=  new Socket("192.168.150.1",10003);

        //2.获取输出流,写数据。
        OutputStream os = s.getOutputStream();//阻塞式方法,有数据才写。
        byte[] bytes = "天降大任于斯人也,比先苦其心志,劳其筋骨,饿其体肤,揉食指然也!".getBytes();
        os.write(bytes);

        //3.释放资源
        s.close();
    }
}

TCP协议接收数据:
A:创建接收端的Socket对象
B:监听客户端连接。返回一个对应的Socket对象
C:获取输入流,读取数据显示在控制台
D:释放资源

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP服务器端Socket对象
        ServerSocket ss = new ServerSocket(10003);

        //2.Socket accept():监听客户端连接,返回一个Socket对象
        Socket s = ss.accept();//听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。

        //3.获取输入流,并打印到控制台
        InputStream is = s.getInputStream();//阻塞式方法,有数据才读。

        byte[] bytes = new byte[1024];
        int len=is.read(bytes);
        System.out.println(new String(bytes,0,len));

        //关闭资源
        //ss.close(); 这个不应该关闭。
        s.close();
    }
}

结果:

天降大任于斯人也,比先苦其心志,劳其筋骨,饿其体肤,揉食指然也!
TCP协议编程服务器获取到数据给一个反馈

TCP协议发送数据:
A:创建发送端的Socket对象
这一步如果成功,就说明连接已经建立成功了。
B:获取输出流,写数据
C:释放资源

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP客户端Socket
        Socket s = new Socket("192.168.150.1", 10004);

        //2.使用Socket对象获取输入流对象
        //写数据
        OutputStream os = s.getOutputStream();
        byte[] bytes = "天降大任于斯人也,比先苦其心志,劳其筋骨,饿其体肤,揉食指然也!".getBytes();
        os.write(bytes);
        //B解决方案
        // s.shutdownOutput();
        //读数据(服务端读取反馈)
        InputStream is = s.getInputStream();
        byte[] bytes2 = new byte[1024];
        int len = is.read(bytes2);//阻塞式方法,有数据才读。
        String str = new String(bytes2, 0, len);
        System.out.println(str);
        //3.释放资源
        s.close();
    }
}

TCP协议接收数据:
A:创建接收端的Socket对象
B:监听客户端连接。返回一个对应的Socket对象
C:获取输入流,读取数据显示在控制台
D:释放资源

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP服务端对象
        ServerSocket ss = new ServerSocket(10004);
        //2.监听客户端连接
        Socket s = ss.accept();//阻塞式方法
        //3.根据Soket对象,获取输入流对象
        //读数据
        InputStream is = s.getInputStream();
        byte[] bytes = new byte[1024];
        //这里段代码有问题?
        //最主要是出现在了while循环的判断上,第一次判断是有数据的,1024个字节完全可以全部接收,数据已经读完了。
        //但是还要判断一下,这时候已经没有数据可以读了,因为is.read(bytes)是一个阻塞式方法根本读不到-1,所以就在这里一直等。
        //数据过来。服务端就不能执行向客户端写数据的代码。所以客户端就看不到“数据收到了字样”
        //解决:
        //A:去掉这里的while循环
        //B:客户端写入数据后,给一个反馈我没有数据了,让服务端不要在等了。
       /* while ((len=is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }*/
        //A解决方案
        int len = is.read(bytes);//阻塞式方法
        System.out.println(new String(bytes, 0, len));

        //4.服务端已经读取到了数据,给一个反馈。
        OutputStream os = s.getOutputStream();
        os.write("数据收到了".getBytes());

        //关闭资源
        //ss.close();//这个不应该关闭的
        s.close();
    }
}

结果:

客户端
		数据收到了
服务端
		天降大任于斯人也,比先苦其心志,劳其筋骨,饿其体肤,揉食指然也!
TCP协议编程客户端键盘录入,服务器输出到控制台。

TCP协议发送数据:
A:创建发送端的Socket对象
这一步如果成功,就说明连接已经建立成功了。
B:获取输出流,写数据
C:释放资源

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP客户端Socket对象
        Socket s = new Socket("192.168.1.134",10011);
        //创建字符输入流对象(模拟键盘录入)
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //2.通过Socket对象获取输入流对象
        //将输入流包装一下
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
        String line = null;
        while ((line = br.readLine()) != null) {
            if("886".equals(line)){
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        //3.释放资源
        s.close();
        bw.close();
        br.close();
    }
}

TCP协议接收数据:
A:创建接收端的Socket对象
B:监听客户端连接。返回一个对应的Socket对象
C:获取输入流,读取数据显示在控制台
D:释放资源

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP服务端的Socket对象
        ServerSocket ss = new ServerSocket(10011);
        //2.监听客户端
        Socket s = ss.accept();//阻塞式方法
        //3.读取数据
        //包装成字符输入流
        //注意:客户端我使用的是字符高效流服务端使用的死字节流
        InputStream is = s.getInputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = is.read(bytes)) != -1) {//阻塞式方法
            System.out.print(new String(bytes, 0, len));
        }
        //释放资源
        s.close();
        //ss.close();  服务器不能停止
    }
}

结果:

客户端
		千锤万找出深山,烈火焚烧若等闲。
		粉身碎骨全不怕,要留清白在人间。
		886
服务端
		千锤万找出深山,烈火焚烧若等闲。
		粉身碎骨全不怕,要留清白在人间。
TCP协议编程客户端键盘录入,服务器输出文本文件。

TCP协议发送数据:
A:创建发送端的Socket对象
这一步如果成功,就说明连接已经建立成功了。
B:获取输出流,写数据
C:释放资源

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP客户端Socket对象
        Socket s = new Socket("192.168.1.134", 11111);
        //获取输入流对象(模拟键盘录入)
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //2.包装Socket输出流对象
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
        //读取数据
        String line = null;
        while ((line = br.readLine()) != null) {
            if ("886".equals(line)) {
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        //3.释放资源
        s.close();
        br.close();
        //  bw.close();s关闭了,相当于bw关掉了。
    }
}

TCP协议接收数据:
A:创建接收端的Socket对象
B:监听客户端连接。返回一个对应的Socket对象
C:获取输入流,读取数据显示在控制台
D:释放资源

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP服务端Socket对象
        ServerSocket ss = new ServerSocket(11111);
        //2.监听客户端
        Socket s = ss.accept();
        //创建字符输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
        //3.包装Socket输入流对象
        BufferedReader br =  new BufferedReader(new InputStreamReader(s.getInputStream()));
        String line = null;
        while ((line=br.readLine())!=null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //4.关闭资源
        bw.close();
        s.close();
        //br.close();s关闭了,这个就也关闭了
        //ss.close();//服务器不能关闭
    }
}

结果:a.txt文件结果

天降大任于斯人也,比先苦其心志,劳其筋骨,饿其体肤,揉食指然而。
且随疾风前行身后亦需留心。
如遇赴死易如反掌。
TCP协议编程客客户端文本文件,服务器输出到控制台

TCP协议发送数据:
A:创建发送端的Socket对象
这一步如果成功,就说明连接已经建立成功了。
B:获取输出流,写数据
C:释放资源

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP客户端Socket对象
        Socket s = new Socket("192.168.1.134", 11111);
        //创建字符输入流对象
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        //2.封装Socket获取输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
        //读取数据
        String line =null;
        while ((line=br.readLine())!=null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        //3.关闭资源
        s.close();
        bw.close();//s已经关闭了,这里就不需要了。
        br.close();
    }
}

TCP协议接收数据:
A:创建接收端的Socket对象
B:监听客户端连接。返回一个对应的Socket对象
C:获取输入流,读取数据显示在控制台
D:释放资源

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        //1.创建TCP服务端Socket对象
        ServerSocket ss  =new ServerSocket(11111);
        //2.监听客户端
        Socket s = ss.accept();
        //3.通过Socket对象获取输入流
        //注意:客户端使用的字符流,服务端使用字节流
        InputStream is = s.getInputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len=is.read(bytes))!=-1){
            System.out.print(new String(bytes,0,len));
        }
        //4.关闭资源
        //ss.close();
        s.close();
    }
}

结果:

天降大任于斯人也,比先苦其心志,劳其筋骨,饿其体肤,揉食指然而。
且随疾风前行身后亦需留心。
如遇赴死易如反掌。
TCP协议编程上传文件,并给出反馈。

TCP协议发送数据:
A:创建发送端的Socket对象
这一步如果成功,就说明连接已经建立成功了。
B:获取输出流,写数据
C:释放资源

出现客户端反馈信息接收不到:

按照正常的思路加入反馈信息,结果却没反应。为什么呢?
读取文本文件是可以以null作为结束信息的,但是呢,通道内是不能这样结束信息的。
所以,服务器根本就不知道你结束了。而你还想服务器给你反馈。所以,就相互等待了。
  
如何解决呢?
  A:在多写一条数据,告诉服务器,读取到这条数据说明我就结束,你也结束吧。
  		这样做可以解决问题,但是不好。(如果文件中刚好有这个标记,那就后面的内容读取不到)
  B:Socket对象提供了一种解决方案
 		public void shutdownOutput()
public class UploadClient {
    public static void main(String[] args) throws IOException {
        //1.创建TCP客户Socket对象
        Socket s =new Socket("192.168.1.134",11112);
        //创建字符输入流对象
        BufferedReader  br = new BufferedReader(new FileReader("zuowen.txt"));
        //2.包装Socket对象
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
        //读数据
        String line = null;
        while((line=br.readLine())!=null){//阻塞
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        //写一个标记
        //bw.write("over"); 这个其实不好,如果文件中的内容有over就会出现,后面的内容读取不到。
        
        //客户端写完数据后,通知服务端不要再等了,我已经没有数据了
        s.shutdownOutput();

        //接收返回反馈信息
        //因为卡在服务端读数据那块,这里也一直在等数据。
        InputStream is = s.getInputStream();
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);//阻塞
        System.out.println(new String(bytes,0,len));
        //3.关闭资源
        s.close();
        br.close();
        //bw.close();s关闭了这个就关闭了
    }
}

TCP协议接收数据:
A:创建接收端的Socket对象
B:监听客户端连接。返回一个对应的Socket对象
C:获取输入流,读取数据显示在控制台
D:释放资源

public class UploadServer {
    public static void main(String[] args) throws IOException {
        //1.创建TCPSocket对象
        ServerSocket ss = new ServerSocket(11112);
        //2.监听客户端
        Socket s = ss.accept();//阻塞
        //创建字符输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.txt"));
        //3.封装Socket对象获取的输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        //读取数据
        //客户端读取不了反馈信息。
        //问题是出现br.readLine()方法上,因为是一个阻塞式方法在流通道内读取不到null,所以就一直在这里
        //等客户端的数据。
        String line = null;
        while ((line= br.readLine())!=null){//阻塞
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //读取完数据后,给一个反馈信息。
        OutputStream os = s.getOutputStream();
        os.write("文件已上传".getBytes());

        //4.关闭资源
        //ss.close(); 服务端不关闭
        s.close();
        bw.close();
        //br.close();s关闭了,这里就不需要关闭了。
    }
}

结果:
Copy文件中的内容

相去万千里,各在天一涯。以前,距离是故园东望路漫漫,
双袖龙钟泪不干的无奈;是羌笛何须怨杨柳,春风不度玉门关的自嘲;
亦是守着窗儿,独自怎生得黑的横亘生死......但在现代科技的飞速发展下,
现在,无论是在东北半球,还是在西南半球,一个skype,一次face...

客户端控制台的内容

文件已上传
TCP协议编程上传图片,并给出反馈。

这里有二个问题就是字节流复制图片,应该也要加上刷新缓冲区不然会出现图片复制不完全:
问题一:
在这里插入图片描述
问题二:
通过while循环可以改进一个服务器接收多个客户端。
但是这个是有问题的。
如果是这种情况,假设我还有张三,李四,王五这三个人分别执行客户端
王五网速很快,但是要等张三和李四执行完。(其实就是因为单线程)
张三:好好学习.avi(100M) 256k
李四:天天向上.mp3(3M) 1M
王五:ILoveJava.txt(1k) 100M

TCP协议发送数据:
A:创建发送端的Socket对象
这一步如果成功,就说明连接已经建立成功了。
B:获取输出流,写数据
C:释放资源

public class UploadClient {
    public static void main(String[] args) throws IOException {
        //1.创建TCP客户端Soket对象
        Socket s = new Socket("192.168.1.134", 11112);
        //创建字节输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("歼20战斗机.jpg"));
        //封装Socket对象输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        //写数据
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = bis.read(bytes)) != -1) {//阻塞
            bos.write( bytes,0,len);
           //防止图片写不全
            bos.flush();
        }
        //通知服务端我已经没数据,不要等待了
        s.shutdownOutput();

        //接收服务端返回数据
        InputStream is = s.getInputStream();
        byte[] bytes2 = new byte[1024];
        int len1 = is.read(bytes2);//阻塞
        System.out.println(new String(bytes2,0,len1));

        //关闭资源
        s.close();
        bis.close();
        //bos.close();s已经关闭,不需要在关闭了。
        //is.close();s已经关闭,不需要在关闭了。
    }
}

TCP协议接收数据:
A:创建接收端的Socket对象
B:监听客户端连接。返回一个对应的Socket对象
C:获取输入流,读取数据显示在控制台
D:释放资源

public class UploadServer {
    public static void main(String[] args) throws IOException {
        //1.创建TCP服务端Socket对象
        ServerSocket  ss = new ServerSocket(11112);
        //2.监听客户端
        Socket s = ss.accept();//阻塞
        //创建字节输入流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mn.jpg"));
        //封装Socket对象获取的输出流
        BufferedInputStream bis  =new BufferedInputStream(s.getInputStream());
        //读数据
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len=bis.read(bytes))!=-1){//阻塞
            bos.write(bytes,0,len);
            //防止图片写不全
            bos.flush();
        }

        //读取完数据给一个反馈
        OutputStream os = s.getOutputStream();
        os.write("图片上传完成".getBytes());//阻塞

        //3.关闭资源
        //ss.close(); 服务端不能关闭
        bos.close();
        s.close();
        //bis.close();s已经关闭了,不需要关闭了
        //os.close();s已经关闭了,不需要关闭了
    }
}

结果:
mn.jpg
在这里插入图片描述
客户端控制台

图片上传完成
TCP协议编程多线程上传图片,并给出反馈。

启动多个线程复制图片

多线程测试

public class ThreadDemo {
    public static void main(String[] args) {
        //创建线程对象
        UploadClient uc = new UploadClient();
        UploadServer us = new UploadServer();

        //启动线程;
        uc.start();
        us.start();
    }
}

TCP协议发送数据:
A:创建发送端的Socket对象
这一步如果成功,就说明连接已经建立成功了。
B:获取输出流,写数据
C:释放资源

public class UploadClient extends Thread {
    @Override
    public void run() {
        try {
        //1.创建TCP客户端Soket对象
        Socket s = new Socket("192.168.1.134", 11112);
        //创建字节输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("歼20战斗机.jpg"));
        //封装Socket对象输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        //写数据
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = bis.read(bytes)) != -1) {
            bos.write( bytes,0,len);
            //防止图片写不全
            bos.flush();
        }
        //通知服务端我已经没数据,不要等待了
        s.shutdownOutput();

        //接收服务端返回数据
        InputStream is = s.getInputStream();
        byte[] bytes2 = new byte[1024];
        int len1 = is.read(bytes2);
        System.out.println(new String(bytes2,0,len1));

        //关闭资源
        s.close();
        bis.close();
        //bos.close();s已经关闭,不需要在关闭了。
        //is.close();s已经关闭,不需要在关闭了。
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

TCP协议接收数据:
A:创建接收端的Socket对象
B:监听客户端连接。返回一个对应的Socket对象
C:获取输入流,读取数据显示在控制台
D:释放资源

public class UploadServer extends Thread {
    @Override
    public void run() {
        try{
            //1.创建TCP服务端Socket对象
            ServerSocket ss = new ServerSocket(11112);
            //2.监听客户端
            Socket s = ss.accept();
            //避免文件名字重复
            String fileName = System.currentTimeMillis()+new Random().nextInt(9999)+".jpg";
            //创建字节输入流
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName));
            //封装Socket对象获取的输出流
            BufferedInputStream bis  =new BufferedInputStream(s.getInputStream());
            //读数据
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len=bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
                //防止图片写不全
                bos.flush();
            }

            //读取完数据给一个反馈
            OutputStream os = s.getOutputStream();
            os.write("图片上传完成".getBytes());

            //3.关闭资源
            //ss.close(); 服务端不能关闭
            bos.close();
            s.close();
            //bis.close();s已经关闭了,不需要关闭了
            //os.close();s已经关闭了,不需要关闭了
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

结果:
在这里插入图片描述
客户端控制台

图片上传完成
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值