JAVA socket编程

资料来源

Java 网络编程 之 socket 的用法与实现

安卓之必须了解的实时通信(Socket)

android进阶3step2:Android App通信 ——端口号IP等网络基础知识扫盲

具体资料参考资料来源1

这里只记录练习过程

基本实现(单向获取客户端输入)

服务端实现

设置监听ip地址(host)和监听端口(ip)并实例化SocketServer

通过accept()方法,阻塞程序,等待socket连接并获取socket实例

通过getInputStream()获取Socket得输入流,处理输入流获取socket携带的信息

对获取信息进行处理

关闭资源(输入流,Socket,Server)

public class DemoServer {

    public static void main(String[] args) throws IOException {

        //默认本机ip 127.0.0.1,通信端口10000
        int port = 10000;

        //实例化server,对指定端口监听
        System.out.println("初始化服务器...");
        ServerSocket server = new ServerSocket(port);

        //阻塞程序,等待socket连接
        System.out.println("服务开始等待连接.....");
        Socket socket = server.accept();

        //获取输入流
        System.out.println("获取输入流并处理...");
        InputStream input = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len;
        StringBuilder sb = new StringBuilder();
        while ((len = input.read(bytes)) != -1) {
            sb.append(new String(bytes, 0, len, "UTF-8"));
        }

        //输出获取的输入信息
        System.out.println("输出获取的输入信息:message=" + sb.toString());

        //关闭资源
        System.out.println("关闭资源...");
        input.close();
        socket.close();
        server.close();
    }
    
}

客户端实现

根据host和port创建Socket连接

获取输出流并输出信息

关闭资源

public class DemoSocket {
    public static void main(String[] args) throws IOException {
        String ip="127.0.0.1";
        int port=10000;

        //建立连接
        Socket socket=new Socket(ip,port);

        //获取输出流,写入信息
        OutputStream output=socket.getOutputStream();
        String message="你好,这是是客户端";
        output.write(message.getBytes("UTF-8"));

        //关闭资源
        output.close();
        socket.close();
    }
}

控制台输出

DemoSocket
建立Socket连接
写入输出流
关闭资源

DemoServer
初始化服务器...
服务开始等待连接.....
获取输入流并处理...
输出获取的输入信息:message=你好,这是是客户端
关闭资源...

服务器和客户端双向通信

服务器实现

在收到客户端信息后,返回信息给客户端

public class DemoServer {

    public static void main(String[] args) throws IOException {

        startServerDouble();
    }

static void startServerDouble() throws IOException {
        //默认本机ip 127.0.0.1,通信端口10000
        int port = 10000;

        //实例化server,对指定端口监听
        System.out.println("初始化服务器...");
        ServerSocket server = new ServerSocket(port);

        //阻塞程序,等待socket连接
        System.out.println("服务开始等待连接.....");
        Socket socket = server.accept();

        //获取输入流
        System.out.println("获取输入流并处理...");
        InputStream input = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len;
        StringBuilder sb = new StringBuilder();
        while ((len = input.read(bytes)) != -1) {
            sb.append(new String(bytes, 0, len, "UTF-8"));
        }

        //输出获取的输入信息
        System.out.println("输出获取的输入信息:message=" + sb.toString());

        //服务器返回信息给客户端,注意统一编码格式
        System.out.println("服务器返回信息给客户端");
        OutputStream output=socket.getOutputStream();
        String message="服务器已收到,请客户端放心";
        output.write(message.getBytes("UTF-8"));

        //关闭资源
        System.out.println("关闭资源...");
        input.close();
        output.close();
        socket.close();
        server.close();
    }

}

客户端实现

在发送信息后,告知服务器发送完毕,接收服务器信息并输出

public class DemoSocket {
    public static void main(String[] args) throws IOException {

        startSocketDouble();
    }

static void startSocketDouble() throws IOException {
        String ip="127.0.0.1";
        int port=10000;

        //建立连接
        System.out.println("建立Socket连接");
        Socket socket=new Socket(ip,port);

        //获取输出流,写入信息
        System.out.println("写入输出流");
        OutputStream output=socket.getOutputStream();
        String message="你好,这是是客户端";
        output.write(message.getBytes("UTF-8"));
        //告知服务器输入信息完毕
        socket.shutdownOutput();

        //获取输入流,并展示
        System.out.println("获取服务器输入流");
        InputStream input=socket.getInputStream();
        StringBuilder sb=new StringBuilder();
        int len;
        byte[] bytes=new byte[1024];
        while ((len=input.read(bytes))!=-1){
            sb.append(new String(bytes,0,len,"UTF-8"));
        }
        System.out.println("服务器输入信息:message="+sb.toString());

        //关闭资源
        System.out.println("关闭资源");
        output.close();
        input.close();
        socket.close();
    }
}

控制台输出

DemoServer
初始化服务器...
服务开始等待连接.....
获取输入流并处理...
输出获取的输入信息:message=你好,这是是客户端
服务器返回信息给客户端
关闭资源...

DemoSocket
建立Socket连接
写入输出流
获取服务器输入流
服务器输入信息:message=服务器已收到,请客户端放心
关闭资源

优化1

如何让服务器和客户端知道什么时候结束输入开始输出,复用同一个socket.

默认soket.close(),或者input.close(),都会导致socket关闭,需要重复建立socket才能继续发送接受

发送完毕,socket.shutdownInputStream(),会告知服务器输入完毕,但是只能继续接受,无法继续发送,要发送信息,需要重新建立socket.

这里采用资源1中推荐的消息长度(两个字节)+消息内容的方式,来利用一个socket持续进行收发消息

单向发送消息

服务器实现

获取输入流后,首先读取两个字节作为长度信息,这里通过位运算符,来处理两个字节,合并为一个长度

public class DemoServer {

    public static void main(String[] args) throws IOException {

        startServerOneWithLength();
    }

static void startServerOneWithLength() throws IOException {
        //默认本机ip 127.0.0.1,通信端口10000
        int port = 10000;

        //实例化server,对指定端口监听
        System.out.println("初始化服务器...");
        ServerSocket server = new ServerSocket(port);

        //阻塞程序,等待socket连接
        System.out.println("服务开始等待连接.....");
        Socket socket = server.accept();

        //获取输入流
        System.out.println("获取输入流并处理...");
        InputStream input = socket.getInputStream();
        while (true) {
            //首先读取两个字节,获取长度
            System.out.println("开始获取信息");
            int fLengthOne = input.read();
            if (fLengthOne == -1) {
                System.out.println("获取客户端输入信息结束");
                break;
            }
            int fLengthSecond = input.read();
            int fLength = (fLengthOne << 8) + fLengthSecond;
            byte[] bytes = new byte[fLength];
            input.read(bytes);

            //输出获取的输入信息
            System.out.println("输出获取的输入信息:message=" + new String(bytes,"UTF-8"));

        }
        //关闭资源
        System.out.println("关闭资源...");
        input.close();
        socket.close();
        server.close();
    }

}

客户端实现

发送信息时,首先计算消息长度,然后通过位运算符分割,先发送消息长度,再发送内容,然后通过flush()刷新并输出缓存区,接着发送下一个消息

public class DemoSocket {
    public static void main(String[] args) throws IOException {

        startSocketOneWithLength();
    }

//单向通信Socket/约定两个字节作为长度
    static void startSocketOneWithLength() throws IOException {
        String ip="127.0.0.1";
        int port=10000;

        //建立连接
        System.out.println("建立Socket连接");
        Socket socket=new Socket(ip,port);

        //获取输出流
        System.out.println("获取输出流");
        //写入信息1
        System.out.println("写入信息1");
        //计算长度,发送时放在前两个字节
        OutputStream output=socket.getOutputStream();
        String message="你好,这是是客户端";
        byte[] bytes=message.getBytes("UTF-8");
        int length=bytes.length;
        output.write(length>>8);
        output.write(length);
        output.write(bytes);
        output.flush();

        //写入信息1
        System.out.println("写入信息2");
        //计算长度,发送时放在前两个字节
        String message2="你好,服务器,这是第二条信息";
        byte[] bytes2=message2.getBytes("UTF-8");
        int length2=bytes2.length;
        output.write(length2>>8);
        output.write(length2);
        output.write(bytes2);
        output.flush();

        //关闭资源
        System.out.println("关闭资源");
        output.close();
        socket.close();
    }
}

控制台输出

DemoServer
开始获取信息
输出获取的输入信息:message=你好,这是是客户端
开始获取信息
输出获取的输入信息:message=你好,服务器,这是第二条信息
开始获取信息
获取客户端输入信息结束
关闭资源...

DemoSocket
建立Socket连接
获取输出流
写入信息1
写入信息2
关闭资源

优化2

实际生产时,不会每次专门为一个socket启动一个服务,处理完socket再关闭.

服务器的实现需要加入多线程处理,线程管理通过线程池,指定线程池大小,超过限制,新的连接进入等待.

服务器实现改造

循环获取socket,并通过线程池分配线程执行

public class DemoServer {

    public static void main(String[] args) throws IOException {

        startServerWithThreadPool();
    }

static void startServerWithThreadPool() throws IOException {
        //默认本机ip 127.0.0.1,通信端口10000
        int port = 10000;

        //实例化server,对指定端口监听
        System.out.println("初始化服务器...");
        ServerSocket server = new ServerSocket(port);

        //启动线程池,等待连接
        System.out.println("服务开始等待连接.....");
        ExecutorService threadPool=Executors.newFixedThreadPool(100);
        //进入循环持续获取socket,并为获得的socket分配线程
        while (true){
            Socket socket = server.accept();
            Runnable runnable=new Runnable() {
                @Override
                public void run() {
                    //获取输入流
                    System.out.println("获取输入流并处理...");
                    try {
                    InputStream input = socket.getInputStream();
                    byte[] bytes = new byte[1024];
                    int len;
                    StringBuilder sb = new StringBuilder();
                    while ((len = input.read(bytes)) != -1) {
                        sb.append(new String(bytes, 0, len, "UTF-8"));
                    }

                    //输出获取的输入信息
                    System.out.println("输出获取的输入信息:message=" + sb.toString());

                    //服务器返回信息给客户端,注意统一编码格式
                    System.out.println("服务器返回信息给客户端");
                    OutputStream output=socket.getOutputStream();
                    String message="服务器已收到,请客户端放心";
                    output.write(message.getBytes("UTF-8"));

                    //关闭资源
                    System.out.println("关闭资源...");
                    input.close();
                    output.close();
                    socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
            threadPool.submit(runnable);
        }

    }
}

客户端实现

采用前文双向通讯实现

控制台输出

打开服务,调用两次socket客户端实现,连续获取两次输出

DemoServer
初始化服务器...
服务开始等待连接.....
获取输入流并处理...
输出获取的输入信息:message=你好,这是是客户端
服务器返回信息给客户端
关闭资源...
获取输入流并处理...
输出获取的输入信息:message=你好,这是是客户端
服务器返回信息给客户端
关闭资源...

 

ps:

inputStream的比较有意思的方法

input.available(),返回从上次read()的位置到下一次可read()的位置之间的内容长度.

实际使用中,BufferedInpuStream来替换InputStream来改善读写性能

DataInputStream用来获取原始数据.

Socket比较有趣的方法

//根据host,port创建socket
String ip="127.0.0.1";
int port=10000;
Socket socket=new Socket(ip,port);

//根据host,port和超时时间创建socket
InetSocketAddress address=new InetSocketAddress("127.0.0.1",10000);
socket.connect(address,10000);

//判断socket是否连接
socket.isConnected()


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import java.io.*; import java.net.*; import java.util.*; import java.lang.*; public class Server extends ServerSocket { private static ArrayList User_List = new ArrayList(); private static ArrayList Threader = new ArrayList(); private static LinkedList Message_Array = new LinkedList(); private static int Thread_Counter = 0; private static boolean isClear = true; protected static final int SERVER_PORT = 10000; protected FileOutputStream LOG_FILE = new FileOutputStream( "d:/connect.log", true); public Server() throws FileNotFoundException, IOException { super(SERVER_PORT); // append connection log // Calendar now = Calendar.getInstance(); // String str = "[" + now.getTime().toString() + // "] Accepted a connection"; // byte[] tmp = str.getBytes(); // LOG_FILE.write(tmp); try { Socket socket = accept(); while (true) { new ServerReaderThread(socket); new ServerWriterThread(socket); } } finally { close(); } } public static void main(String[] args) throws IOException { new Server(); } // --- CreateServerThread class ServerReaderThread extends Thread { private Socket client; private BufferedReader in; private PrintWriter out; private String Username; public ServerReaderThread(Socket s) throws IOException { client = s; in = new BufferedReader(new InputStreamReader(client .getInputStream())); out = new PrintWriter(client.getOutputStream(), true); start(); } public void run() { try { int flag = 0; Thread_Counter++; String line = in.readLine(); while (!line.equals("bye")) { out.println(line); line = in.readLine(); } out.println("--- See you, bye! ---"); // System.out.println("--- See you, bye! ---"); client.close(); } catch (IOException e) { } finally { try { client.close(); } catch (IOException e) { } Thread_Counter--; } } } // --- CreateServerThread class ServerWriterThread extends Thread { priva

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值