Java-socket编程(建立连接)

socket编程是以IO为理论基础的,理论学得差不多也很难实现编程,毕竟里面的类和方法平时都不怎么用,难得尝试编了个程,记录一下。

1.几个概念

Channel:管道,连通客户端和服务端传输数据;

Buffer:缓冲区,通过管道传输数据必须经过的地方;

Selector:选择器,单线程可以通过选择器处理多个管道;本文没有使用;

2.案例功能

启动服务端,再启动客户端与服务端连接,在客户端的控制台输入命令获取服务端的效果。本样例只处理了“time”命令,获取服务端的当前时间,其他命令都返回“非指定命令,无返回值”。详看代码注释。

3.服务端代码

 1 import java.net.InetSocketAddress;
 2 import java.nio.ByteBuffer;
 3 import java.nio.channels.ServerSocketChannel;
 4 import java.nio.channels.SocketChannel;
 5 import java.nio.charset.StandardCharsets;
 6 import java.util.Date;
 7 import java.util.LinkedList;
 8 
 9 public class NIOService {
10     static int PORT = 9011;
11     public static void main(String[] args) throws Exception{
12         //存储客户端连接
13         LinkedList<SocketChannel> clients = new LinkedList<>();
14         //1.服务端开启监听:接受客户端
15         ServerSocketChannel ss = ServerSocketChannel.open();
16         ss.bind(new InetSocketAddress(PORT));
17         //2.只接受客户端,不阻塞
18         ss.configureBlocking(false);
19 
20         while (true) {
21             // 接受客户端的连接
22             // client在Java层面是一个对象,在内核层面是一个fd
23             SocketChannel client = ss.accept();
24             if (client == null) {
25                 //while循环进来没有 连到客户端就不管
26             } else {
27                 //和client传输数据使用的socket->fd
28                 client.configureBlocking(false);
29                 //获取客户端的端口号
30                 int port = client.socket().getPort();
31                 System.out.println("接收到客户端的连接,client port: " + port);
32                 //将客户端添加到列表里
33                 clients.add(client);
34             }
35             //可以在堆里,堆外,相关内容,可以看看JVM直接内存
36             ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
37 
38             //遍历已经链接进来的客户端 的管道channel里有没有数据
39             for (SocketChannel c : clients) {
40                 //每循环一次都是一次系统调用,都是一次用户内核态的切换
41                 int num = c.read(buffer);
42                 if (num > 0) {
43                     buffer.flip();
44                     byte[] bytes = new byte[buffer.limit()];
45                     buffer.get(bytes);
46                     String s = new String(bytes);
47                     System.out.println("端口为"+c.socket().getPort() + "的客户端发来命令:" +s);
48                     String res = "";
49                     ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4096);
50                     if("time".equals(s)){
51                         //获取时间
52                         long l = System.currentTimeMillis();
53                         res = "time: "+new Date(l).toString();
54                         System.out.println(res);
55                     }else{
56                         res = "非指定命令,无返回值";
57                     }
58                     byteBuffer.put(res.getBytes(StandardCharsets.UTF_8));
59                     byteBuffer.flip();
60                     c.write(byteBuffer);
61                 }
62                 buffer.clear();
63             }
64         }
65     }
66 }

 4.客户端代码

 1 import java.io.*;
 2 import java.net.Socket;
 3 import java.nio.channels.ServerSocketChannel;
 4 import java.nio.channels.SocketChannel;
 5 import java.util.Scanner;
 6 
 7 public class Client {
 8     public static void main(String[] args) {
 9         try {
10             //建立一个客户端连到9010的服务端
11             Socket client = new Socket("127.0.0.1",9011);
12             //设置发送命令的长度
13             client.setSendBufferSize(20);
14             /**
15              * 关闭Nagle算法:该算法是将多个命令打包一起发送给服务端,避免网络拥挤。
16              * 但是现在网络宽松,随便发也没事。所以关闭。
17              */
18             client.setTcpNoDelay(false);
19 
20             OutputStream out = client.getOutputStream();
21             InputStream input = client.getInputStream();
22             InputStream in = System.in;
23             BufferedReader reader = new BufferedReader(new InputStreamReader(in));
24             Scanner scan = new Scanner(System.in);
25             while(true){
26                 String line = scan.nextLine();
27                 //String line = reader.readLine();
28                 if(line != null ){
29                     //将输入的命令用字节数组存起来,通过客户端client的输出流发送给服务端
30                     byte[] bb = line.getBytes();
31                     out.write(bb);
32                 }
33                 byte b[] = new byte[1024];
34                 /**
35                  从输入流里读 东西 到 b数组,如果没有东西,就会一直读,阻塞。
36                  len获取当前输入流里的内容长度,英文占1个长度,中文占3个长度
37                  */
38                 int len = input.read(b);
39                 System.out.println(new String(b));
40             }
41         } catch (IOException e) {
42             e.printStackTrace();
43         }
44     }
45 
46 }

 

posted @ 2021-03-11 10:54  守林鸟  阅读(2186)  评论(0编辑  收藏  举报