TCP流套接字编程

举报
bug郭 发表于 2022/09/30 23:47:13 2022/09/30
【摘要】 大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流作者简介:CSDN java领域新星创作者blog.csdn.net/bug…掘金LV3用户 juejin.cn/user/bug…阿里云社区专家博主,星级博主,developer.a...

大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流

作者简介:

TCP流套接字编程

我们知道TCP是有连接的!
所以我们需要先对客户端建立连接!
我们来学习一下TCP协议对应的Socket API!

我们先来学习两个类:
通过这两个类我们就可以对TCP协议网络编程!

这两个类也在java.net包下!
在这里插入图片描述

  • ServerSocket
    这个类实现了服务器套接字。

服务器套接字等待通过网络进入的请求。 它根据该请求执行一些操作,然后可能将结果返回给请求者。
服务器套接字的实际工作由SocketImpl类的实例执行。 应用程序可以更改创建套接字实现的套接字工厂,以配置自己创建适合本地防火墙的套接字!
构造方法:
在这里插入图片描述

  • ServerSocket()
    创建未绑定的服务器套接字。
  • ServerSocket(int port)
    创建绑定到指定端口的服务器套接字。
  • ServerSocket(int port, int backlog)
    创建服务器套接字并将其绑定到指定的本地端口号,并指定了积压。
  • ServerSocket(int port, int backlog, InetAddress bindAddr)
    创建一个具有指定端口的服务器,侦听backlog和本地IP地址绑定。

我们主要用到的是第二个构造方法!我们的服务器一般都需要绑定端口号,便于客户端访问!
这个类一般用于服务器程序!

需要用到的方法
在这里插入图片描述

  • Socket accept()
    倾听要连接到此套接字并接受它!

通过这个方法,我们就可以与客户端建立连接!
返回的Socket对象,我们后面的操作就对socket对象进行就好了!
ServerSocket对象就完成了他的任务和使命了!

就好比之前的电话接线员,连接后就和他没有关系了!剩下的就交个两个打电话的人了!这里的也是剩下的就交给socket对象就好了!

用通俗的话讲就是,这里的ServerSocket就做了一件事情,通过网络请求,与客户端建立连接,执行完后就将结果返回给请求者!

  • Socket

该类实现客户端套接字(也称为“套接字”)。 套接字是两台机器之间通讯的端点。
套接字的实际工作由SocketImpl类的实例执行。 应用程序通过更改创建套接字实现的套接字工厂,可以配置自己创建适合本地防火墙的套接字。
这里的Socket对象就相当于接电话的两个人,下面的通讯操作,主要通过这个类来实现!

构造方法
在这里插入图片描述

  • Socket()
    创建一个未连接的套接字,并使用系统默认类型的SocketImpl。
  • Socket(InetAddress address, int port)
    创建流套接字并将其连接到指定IP地址的指定端口号。

我们主要学习这两个构造方法!
第一个无参构造,一般用于服务器!当ServerSocket对象调用accept方法后就可以用这个对象接收!因为这个socket对象是来自客户端,所有已经有了端口号!

而第二个构造方法一般用于创建服务器socket!
注意:

这里的addressport并不是像UDP一样设置直接的端口号,而是连接到这个IP和端口号的服务器!

需要用到的方法

  • void close()
    关闭此套接字。
  • InetAddress getInetAddress()
    返回套接字所连接的地址。
  • InetAddress getLocalAddress()
    获取套接字所绑定的本地地址。

这里一个是连接的服务器地址,一个是自己的地址!

  • int getLocalPort()
    返回此套接字绑定到的本地端口号。
  • InputStream getInputStream()
    返回此套接字的输入流。
  • OutputStream getOutputStream()
    返回此套接字的输出流。

我们上面的这两个方法就实现了TCP面向字节流!

通过操作上面的两个对象就可以实现通信,输入和输出了!

TCP面向字节流网络编程案例

  • 回显服务客户端服务器
//回显服务服务器
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoServer {
    //1.ServerSocket 只负责建立连接!
    ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true){
            //因为TCP是有连接的,我们要先建立连接(接电话)
            //ServerSocket只做了一件事情,调用accept方法建立连接
            //我们就得到了socketClient客户!!!
            //后面就只需要针对socketClient进行操作即可!
            //如果没有客户端连接,accept就会阻塞!
            Socket socketClient = serverSocket.accept();
            processConnection(socketClient);
        }
    }

    public void processConnection(Socket socketClient) {
        //TCP面向字节流,所以直接通过文件操作,便可以将数据读写!
        try(InputStream inputStream = socketClient.getInputStream()) {
            //调用getInputStrem方法获取到请求,用输入字节流接收!
            try(OutputStream outputStream = socketClient.getOutputStream()){
                //调用getOutStrem方法,用于输出应答!
                //通过scanner读取请求更便利!
                Scanner scanner = new Scanner(inputStream);
                //循环处理每一个请求,返回对应响应!
                while(true){
                    if(!scanner.hasNext()){
                        System.out.printf("[客户端IP:%s,port:%d] 退出连接\n",socketClient.getInetAddress(),socketClient.getPort());
                        break;
                    }
                    //获取到请求
                    String request = scanner.next();
                    //根据请求给出应答
                    String response = process(request);
                    //把这个响应给客户端!
                    //通过printwiter包裹OutputSteam!
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    //将请求字符串写入到输出流中!
                    printWriter.println(response);
                    printWriter.flush();//刷新缓冲器,让客户端立即获取到响应!
                    System.out.printf("[客户端IP:%s,port:%d] req:%s,res:%s\n",socketClient.getInetAddress(),socketClient.getPort(),request,response);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭资源!
                socketClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public String process(String request) {
        //回显
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
}

//回显服务客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoClient {
    private Socket socket = null;
    public TcpEchoClient(String serverIp,int serverPort) throws IOException {
        //客户端
        //这里的IP和端口是服务器的,并且这里传入IP和port表示与这个服务器建立连接!
        socket = new Socket(serverIp,serverPort);
    }
    public void start(){
        System.out.println("和服务器连接成功!");
        Scanner scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream()){
            try(OutputStream outputStream = socket.getOutputStream()){
                while(true){
                    //1.从控制台读取请求
                    System.out.print("->");
                    String request = scanner.next();
                    //2.构造请求并发送给服务器
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(request);
                    printWriter.flush();
                    //3.读取响应
                    Scanner scannerResponse = new Scanner(inputStream);
                    String response = scannerResponse.next();
                    //把结果打印到控制台!
                    System.out.printf("req:%s,res:%s\n",request,response);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭资源
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws IOException {
      TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9090);
      tcpEchoClient.start();
    }
}

启动服务器:
在这里插入图片描述
启动客户端:
在这里插入图片描述
在这里插入图片描述
可以看到,这里我们也实现了回显服务!

当我们多开几个客户端试试!
在这里插入图片描述
在这里插入图片描述
可以看到客户端并没有收到应答,服务器也没和客户端建立连接!

因为,我们的ServerSocket对象,调用了accept后就一直在循环中,如果当前客户端不和服务器断开连接的话,就不会和其他客户端建立连接了!
在这里插入图片描述
我们如何才能实现多客户端呢?

我们回顾之前的对线程操作!我们可以有多个线程同时对多个客户端建立连接!!!
那我们进行升级一下变成多进程版本的服务器!

//多线程版本服务器
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TcpThreadEchoServer{
    //1.ServerSocket 只负责建立连接!
    ServerSocket serverSocket = null;
    public TcpThreadEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true){
            //因为TCP是有连接的,我们要先建立连接(接电话)
            //ServerSocket只做了一件事情,调用accept方法建立连接
            //我们就得到了socketClient客户!!!
            //后面就只需要针对socketClient进行操作即可!
            //如果没有客户端连接,accept就会阻塞!
            Thread thread = new Thread(()->{
                Socket socketClient = null;
                try {
                    socketClient = serverSocket.accept();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                processConnection(socketClient);
            });
           thread.start();
        }
    }
    public void processConnection(Socket socketClient) {
        //TCP面向字节流,所以直接通过文件操作,便可以将数据读写!
        try(InputStream inputStream = socketClient.getInputStream()) {
            //调用getInputStrem方法获取到请求,用输入字节流接收!
            try(OutputStream outputStream = socketClient.getOutputStream()){
                //调用getOutStrem方法,用于输出应答!
                //通过scanner读取请求更便利!
                Scanner scanner = new Scanner(inputStream);
                //循环处理每一个请求,返回对应响应!
                while(true){
                    if(!scanner.hasNext()){
                        System.out.printf("[客户端IP:%s,port:%d] 退出连接\n",socketClient.getInetAddress(),socketClient.getPort());
                        break;
                    }
                    //获取到请求
                    String request = scanner.next();
                    //根据请求给出应答
                    String response = process(request);
                    //把这个响应给客户端!
                    //通过printwiter包裹OutputSteam!
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    //将请求字符串写入到输出流中!
                    printWriter.println(response);
                    printWriter.flush();//刷新缓冲器,让客户端立即获取到响应!
                    System.out.printf("[客户端IP:%s,port:%d] req:%s,res:%s\n",socketClient.getInetAddress(),socketClient.getPort(),request,response);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭资源!
                socketClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public String process(String request) {
        //回显
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpThreadEchoServer tcpThreadEchoServer = new TcpThreadEchoServer(9090);
        tcpThreadEchoServer.start();
    }
}

在这里插入图片描述
我们只更改了一点代码就实现了多个客户端!

我们来学习了线程池,我们在更改成线程池版本!

//线程池版本服务器
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpThreadPoolEchoServer {
    //1.ServerSocket 只负责建立连接!
    ServerSocket serverSocket = null;
    public TcpThreadPoolEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService pool = Executors.newCachedThreadPool();
        while(true){
            //因为TCP是有连接的,我们要先建立连接(接电话)
            //ServerSocket只做了一件事情,调用accept方法建立连接
            //我们就得到了socketClient客户!!!
            //后面就只需要针对socketClient进行操作即可!
            //如果没有客户端连接,accept就会阻塞!

            //线程池版本!
            Socket socketClient = serverSocket.accept();
            pool.submit(new Runnable() {
                @Override
                public void run() {

                }
            });
            processConnection(socketClient);
        }
    }

    public void processConnection(Socket socketClient) {
        //TCP面向字节流,所以直接通过文件操作,便可以将数据读写!
        try(InputStream inputStream = socketClient.getInputStream()) {
            //调用getInputStrem方法获取到请求,用输入字节流接收!
            try(OutputStream outputStream = socketClient.getOutputStream()){
                //调用getOutStrem方法,用于输出应答!
                //通过scanner读取请求更便利!
                Scanner scanner = new Scanner(inputStream);
                //循环处理每一个请求,返回对应响应!
                while(true){
                    if(!scanner.hasNext()){
                        System.out.printf("[客户端IP:%s,port:%d] 退出连接\n",socketClient.getInetAddress(),socketClient.getPort());
                        break;
                    }
                    //获取到请求
                    String request = scanner.next();
                    //根据请求给出应答
                    String response = process(request);
                    //把这个响应给客户端!
                    //通过printwiter包裹OutputSteam!
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    //将请求字符串写入到输出流中!
                    printWriter.println(response);
                    printWriter.flush();//刷新缓冲器,让客户端立即获取到响应!
                    System.out.printf("[客户端IP:%s,port:%d] req:%s,res:%s\n",socketClient.getInetAddress(),socketClient.getPort(),request,response);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭资源!
                socketClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public String process(String request) {
        //回显
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpThreadPoolEchoServer tcpThreadPoolEchoServer = new TcpThreadPoolEchoServer(9090);
        tcpThreadPoolEchoServer.start();
    }
}

我们对TCPUDP socket API网络编程的学习就到此为止,还有其他的内容,我们下次学习!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。