Socket类关于TCP字符流编程的理解学习

基本介绍

套接字(socket):是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口

  1. socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。
  2. socket简单的说,由 端口,协议,地址三个组成部分。
  3. 套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
  4. 由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。
  5. 但在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。
  • Socket主要用于开发网络应用程序,其背广泛采用

  • 通信的两端都需要有Socket,是两台机器间通信的断点

  • 网络通信其实就是Socket的通信

  • Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输

  • 一般主动发起通信的应用程序属于客户端,等待通信请求的为服务端

示意图:

image-20220727232736510

代码实现

一定要注意,必须要先开启服务端,在启动客户端,否则会报错

**注意:**这里的代码示例都是关于发送与接收信息,如果需要发送或接收图片等都差不多,只是必须要使用字节流而不能使用字符流,基本流程都是使用IO中的知识。

需求1:

  1. 编写一个服务端和一个客户端
  2. 服务端在9999端口监听
  3. 客户端连接到服务器,发送"hello,server",退出
  4. 服务端接收到客户端发送的信息并输出,退出

服务端

/**
 *
 * 服务端
 * 需求:接收从客户端发来的信息并输出
 *
 * @author:雷子杰
 * @date:2022/7/27
 */
public class ScoketTCP01Server {
    public static void main(String[] args) throws IOException {

        //新建serverSocket对象,实现9999端口监听
        ServerSocket serverSocket = new ServerSocket(9999);

        //客户端连接后会生成管道
        Socket socket = serverSocket.accept();

        //获取管道的输入流
        InputStream inputStream = socket.getInputStream();
        byte[] buf=new byte[1024];
        int len = 0;

        while ((len = inputStream.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
        }

        //关闭流
        inputStream.close();
        socket.close();
        serverSocket.close();


    }
}

客户端

输出流写入数据后一定要进行flush(),同时进行结束标记

/**
 *
 * 客户端
 * 需求:向服务器端发送一条信息
 *
 * @author:雷子杰
 * @date:2022/7/27
 */
public class ScoketTCP01Client {
    public static void main(String[] args) throws IOException {
        //获取socket连接
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);

        //获取输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //往输出流中写入信息
        outputStream.write("hello,server".getBytes());

        //关闭流
        outputStream.close();
        socket.close();
    }
}

测试结果

服务端收到信息

image-20220727231736859

需求2

  1. 编写一个服务端和一个客户端
  2. 服务端在9999端口监听
  3. 客户端连接到服务器,发送"hello,server",并接收服务端回发的"hello,client",退出
  4. 服务端接收到客户端发送的信息输出,并发送"hello,client",退出

服务端

输出流写入数据后一定要进行flush(),同时进行结束标记

/**
 *
 * 服务端
 * 需求:接收从客户端发来的信息输出,并返回一条消息给客户端
 *
 *
 * @author:雷子杰
 * @date:2022/7/27
 */
public class ScoketTCP02Server {
    public static void main(String[] args) throws IOException {

        //新建serverSocket对象,实现9999端口监听
        ServerSocket serverSocket = new ServerSocket(9999);

        //客户端连接后会生成管道
        Socket socket = serverSocket.accept();

        //获取管道的输入流,获取客户端发来的信息
        InputStream inputStream = socket.getInputStream();
        byte[] buf=new byte[1024];
        int len = 0;

        while ((len = inputStream.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
        }
        //关闭读取
        socket.shutdownInput();

        //获取输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //往输出流中写入信息,发送给服务端
        outputStream.write("hello,client".getBytes());
        //关闭写入
        socket.shutdownOutput();

        //关闭流
        outputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();


    }
}

客户端

输出流写入数据后一定要进行flush(),同时进行结束标记

/**
 *
 * 客户端
 * 需求:向服务器端发送一条信息,并接收从服务器端返回的一条信息
 *
 * @author:雷子杰
 * @date:2022/7/27
 */
public class ScoketTCP02Client {
    public static void main(String[] args) throws IOException {
        //获取socket连接
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);

        //获取输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //往输出流中写入信息,发送给服务端
        outputStream.write("hello,server".getBytes());
        //关闭写入
        socket.shutdownOutput();

        //获取管道的输入流,获取服务器端发来的信息
        InputStream inputStream = socket.getInputStream();
        byte[] buf=new byte[1024];
        int len = 0;

        while ((len = inputStream.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
        }

        //关闭流
        inputStream.close();
        outputStream.close();
        socket.close();
    }
}

测试结果

服务端收到信息

image-20220727231852931

客户端收到信息

image-20220727231909658

需求3

使用字符流完成,之前两个需求都是使用的字节流去完成

  1. 编写一个服务端和一个客户端
  2. 服务端在9999端口监听
  3. 客户端连接到服务器,发送"hello,server",并接收服务端回发的"hello,client",退出
  4. 服务端接收到客户端发送的信息输出,并发送"hello,client",退出

服务端

输出流写入数据后一定要进行flush(),同时进行结束标记

/**
 *
 * 服务端
 * 需求:使用字符流,接收从客户端发来的信息输出,并返回一条消息给客户端
 *
 * @author:雷子杰
 * @date:2022/7/27
 */
public class ScoketTCP03Server {
    public static void main(String[] args) throws IOException {

        //新建serverSocket对象,实现9999端口监听
        ServerSocket serverSocket = new ServerSocket(9999);

        //客户端连接后会生成管道
        Socket socket = serverSocket.accept();

        //获取管道的输入流,获取客户端发来的信息
        InputStream inputStream = socket.getInputStream();
        //将字节流输入流转为字符流输入流
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        char[] cbuf=new char[1024];
        int len;
        while ((len = inputStreamReader.read(cbuf))!=-1){
            System.out.println(new String(cbuf,0,len));
        }
        //关闭读取
        socket.shutdownInput();

        //获取输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //将字节流输出流转换为字符流输出流
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        //往输出流中写入信息,发送给服务端
        outputStreamWriter.write("hello,client");
        //需要刷新流后才能写入
        outputStreamWriter.flush();
        //关闭写入
        socket.shutdownOutput();

        //关闭流
        outputStreamWriter.close();
        inputStreamReader.close();
        socket.close();
        serverSocket.close();


    }
}

客户端

输出流写入数据后一定要进行flush(),同时进行结束标记

/**
 *
 * 客户端
 * 需求:使用字符流,向服务器端发送一条信息,并接收从服务器端返回的一条信息
 *
 * @author:雷子杰
 * @date:2022/7/27
 */
public class ScoketTCP03Client {
    public static void main(String[] args) throws IOException {
        //获取socket连接
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);

        //获取输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //将字节流输出流转换为字符流输出流
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        //往输出流中写入信息,发送给服务端
        outputStreamWriter.write("hello,server");
        //需要刷新流后才能写入
        outputStreamWriter.flush();

        //关闭写入
        socket.shutdownOutput();

        //获取管道的输入流,获取服务器端发来的信息
        InputStream inputStream = socket.getInputStream();
        //将字节流输入流转为字符流输入流
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        char[] cbuf=new char[1024];
        int len;
        while ((len = inputStreamReader.read(cbuf))!=-1){
            System.out.println(new String(cbuf,0,len));
        }

        //关闭流
        inputStreamReader.close();
        outputStreamWriter.close();
        socket.close();
    }
}

测试结果

服务端收到信息

image-20220727231959710

客户端收到信息

image-20220727231954226

发送图片、视频等文件流程说明

由于是图片、视频等文件,所以一定要使用字节流去进行传输

需求:

  1. 编写一个服务端和一个客户端
  2. 服务端在8888端口监听
  3. 客户端连接到服务端,发送本地一张图片
  4. 服务端接收到客户端发送的图片,保存到src下,发送“收到图片”给客户端,然后退出
  5. 客户端接收服务端发送的“收到图片”,然后退出

流程示例:

  1. 客户端使用字节输入流将磁盘中的图片输入到内存中
  2. 客户端使用字节输出流将内存中的图片输出给服务端
  3. 服务端使用字节输入流将客户端字节输出流中的图片获取得到
  4. 服务端使用字节输出流将图片输出到磁盘文件中

服务端

输出流写入数据后一定要进行flush(),同时进行结束标记

/**
 *
 * 服务端
 *
 * @author:雷子杰
 * @date:2022/7/27
 */
public class TCPFileUploadServer {
    public static void main(String[] args) throws IOException {

        //监听8888端口
        ServerSocket serverSocket = new ServerSocket(8888);
        //等待连接
        Socket socket = serverSocket.accept();

        BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
        //创建的字节流输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("basic_review\\src\\socket_\\图片.jpg"));
        //读取图片信息
        byte[] buf = new byte[1024];
        int len;
        while ((len = bufferedInputStream.read(buf)) != -1){

            bufferedOutputStream.write(buf,0,len);
        }
        bufferedOutputStream.flush();

        socket.shutdownInput();



        bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());

        bufferedOutputStream.write("收到图片".getBytes());

        //刷新流
        bufferedOutputStream.flush();

        socket.shutdownOutput();

        bufferedOutputStream.close();
        bufferedInputStream.close();
        socket.close();
        serverSocket.close();

    }
}

客户端

输出流写入数据后一定要进行flush(),同时进行结束标记

/**
 *
 * 客户端
 *
 * @author:雷子杰
 * @date:2022/7/27
 */
public class TCPFileUploadClient {
    public static void main(String[] args) throws IOException {
        //连接主机的8888端口,得到socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);

        //读取的文件地址
        String filePath ="f:\\ssm博客系统.jpg";
        //创建的字节流输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
        //创建的字节流输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
        //读取图片信息
        byte[] buf = new byte[1024];
        int len;
        while ((len = bufferedInputStream.read(buf)) != -1){
            //写入到管道中
            bufferedOutputStream.write(buf,0,len);
        }
        //刷新流
        bufferedOutputStream.flush();
        //关闭写出
        socket.shutdownOutput();


        bufferedInputStream = new BufferedInputStream(socket.getInputStream());
        while ((len = bufferedInputStream.read(buf)) != -1){
            System.out.println(new String(buf,0,len));

        }

        socket.shutdownInput();

        bufferedOutputStream.close();
        bufferedInputStream.close();

        socket.close();


    }
}

总结

目前复习java基础复习到了计算机网络阶段,发现自身在网络通信编程这方面的知识较为匮乏,本篇文章用于记载学习java网络编程技术

另外一个重点!!!!!在使用输出流写入数据后一定要进行flush(),同时进行结束标记,否则可能会报错

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
西北师范大学计算机科学与工程学院 学生实验报告 学号 日期 : "系别 "计算机科学 "专业 " "班级 " "姓 名 " " " "与工程学院 " " " " " " " "课程 " "课程 " "学时数 "2 " "名称 " "型 " " " " "实验 "实验二、基于TCP的服务器/客户端编程 " "名称 " " "实验目的:1、掌握Linux下的TCP客户端基本原理和基本编程方法 " " " "实验内容: " "1、写Linux下TCP服务器套接字程序,程序运行时服务器等待客户的连接,一旦连接 " "成功,则显示客户的IP地址、端口号,并向客户端发送字符串。 " "2、写Linux下TCP客户端套接字程序,结合TCP的服务器端程序,实现以下功能: " "(1)、客户根据用户提供的IP地址连接到相应的服务器; " "(2)、服务器等待客户的连接,一旦连接成功,则显示客户的IP地址、端口号,并 " "向客户端发送字符串; " "(3)、客户接收服务器发送的信息并显示。 " "实验步骤: " "TCP服务端程序设计 " "使用TCP套接字编程可以实现基于TCP/IP协议的面向连接的通信,它分为服务器端和 " "客户端两部分,其主要实现过程如图所示。 " " " " " " 图1.1 TCP客户/服务器的套接字函数 " " " "socket函数:为了执行网络输入输出,一个进程必须做的第一件事就是调用socket函" "数获得一个文件描述符。 " " #include <sys/socket.h> " " int socket(int family,int type,int protocol);     " "     返回:非负描述字---成功   -1---失败 " "  第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)" "和AF_INET6(IPv6协议);第二个参数指明套接口型,有三种型可选:SOCK_STREA" "M(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);如果套接 " "口型不是原始套接口,那么第三个参数就为0。 " "2、connect函数:当用socket建立了套接口后,可以调用connect为这个套接字指明" "远程端的地址;如果是字节流套接口,connect就使用三次握手建立一个连接;如果 " "是数据报套接口,connect仅指明远程端地址,而不向它发送任何数据。 " "#include <sys/socket.h>       " " int connect(int sockfd, const struct sockaddr * addr, socklen_t " "addrlen);   " "           返回:0---成功   -1---失败 " "  第一个参数是socket函数返回的套接口描述字;第二和第三个参数分别是一个指" "向套接口地址结构的指针和该结构的大小。 " "这些地址结构的名字均已"sockaddr_"开头,并以对应每个协议族的唯一后缀结束。 " "以IPv4套接口地址结构为例,它以"sockaddr_in"命名,定义在头文件<netinet/in.h" ">;以下是结构体的内容: " "struct in_addr " "{ " " in_addr_t s_addr;     " "}; " "struct sockaddr_in { " " uint8_t sin_len; " " sa_family_t sin_family; " " in_port_t sin_port; " " struct in_addr sin_addr; " " char sin_zero[8]; " "}; " "bind函数:为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IP" "v4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind" "时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才" "选择一个本地IP地址。 " "#include <sys/socket.h>   " " int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen); " " 返回:0---成功   -1---失败  " " " "  第一个参数是socket函数返回的套接口描述字;第二和第第三个参数分别是一个" "指向特定于协议的地址结构的指针和该地址结构的长度。 " "listen函数:listen函数仅被TCP服务器调用,它的作用是将用soc

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱学习的大雄

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

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

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

打赏作者

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

抵扣说明:

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

余额充值