JDK BIO - Socket编程

记得之前我去培训Android时,有一个Socket编程面试题目是:

使用Socket编程实现文件传输

当时的自己很单纯,想着什么都是自己做,不去查怎么做。记得文件内容传输的实现很简单,但是文件名称该怎么传却难到了我,在难也要自己做,后面多建立了一条Socket专门用来传输文件名。

Socket编程好久没用到过了,了解NIO的时候,看到了Socket,所以特地回忆下:

网络图片

Socket

Socket编程根据通信协议的不同可以分为UDP(数据通信协议),TCP(流通信协议)/IP两种;

UDP协议用到的API: DatagramSocket

TCP/IP协议用到的API :ServerSocket

DatagramSocket

UDP是一种无连接的协议,这就意味着我们每次发送数据报时,需要同时发送本机的socket描述符和接收端的socket描述符,而且其在大小上有64KB的限制,是一种不可靠的协议,发送的数据报不一定会按照其发送顺序被接收端的socket接受;

这里主要是想总结TCP/IP,这里就直接给贴出网上找的代码

服务器端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  public static void main(String[] args) throws Exception{
      int serverPort = 9999;
      DatagramSocket ds = null;
      DatagramPacket sendDp;
      DatagramPacket receiveDp;

      ds = new DatagramSocket(serverPort);
      System.out.println("服务器创建成功!端口号为: "+ds.getLocalPort());

      byte[] buf = new byte[1024];
      receiveDp = new DatagramPacket(buf,buf.length);
      ds.receive(receiveDp);
      System.out.println("收到: "+ receiveDp.getSocketAddress());
      System.out.println("Data is "+ new String(receiveDp.getData(),0,receiveDp.getLength()));

      InetAddress clientIp = receiveDp.getAddress();
      int clientPort = receiveDp.getPort();

      String respose = "OK,收到来自星星的你的祝福";
      byte[] bData = respose.getBytes();
      sendDp = new DatagramPacket(bData,bData.length,clientIp,clientPort);
      ds.send(sendDp);
      ds.close();
  }

客户器端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws Exception{
      DatagramSocket ds = null;//
      DatagramPacket sendDp;
      DatagramPacket receiveDp;
      String serverHost = "127.0.0.1";
      int serverPort = 9999;
      ds = new DatagramSocket();
      byte[] buf = "hello,UDP协议!来自星星的问候……".getBytes();
      sendDp = new DatagramPacket(buf,buf.length,InetAddress.getByName(serverHost),serverPort);

      ds.send(sendDp);

      byte[] bufr = new byte[1024];
      receiveDp = new DatagramPacket(bufr,bufr.length);
      ds.receive(receiveDp);

      byte[] response = receiveDp.getData();
      int len = receiveDp.getLength();
      String s = new String(response,0,len);
      System.out.println("服务器端反馈为: "+s);
      ds.close();
  }

ServerSocket

直接使用线程BIO

如果使用Socket编程,编写服务端,如果客户端有多个,那么编程模型就是1:N的关系;

服务端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Server {

public void start() throws IOException {

ServerSocket serverSocket = new ServerSocket(9091);
System.out.println("∂ 服务真在等待客户端接入请求");
for (; ; ) {
System.out.println("∂ for accept 之前");
Socket socket = serverSocket.accept();
new Thread(new ScoketChanil(socket)).start();
}
}

public static void main(String[] args) throws IOException {
new Server().start();
}
}


public class ScoketChanil implements Runnable {

public Socket socket;
public ScoketChanil(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
byte[] bytes = new byte[1024];

int len;
while ((len = inputStream.read(bytes)) != -1) {
String requestStr = new String(bytes, 0, len);
System.out.println(requestStr);
outputStream.write("yeah im copy".getBytes("UTF-8"));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class MultipleClientA {
public static void main(String[] args) throws IOException {
new Client("AAAA").start();
}
}

public class Client {

private String clientName;

public Client(String clientName) {
this.clientName = clientName;
}

public void start() throws IOException {
Socket socket = new Socket("127.0.0.1", 9091);
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();

new Thread(new Runnable() {
@Override
public void run() {
byte[] bytes = new byte[1024];
int len;
try {
while ((len = inputStream.read(bytes)) != -1) {
String s = new String(bytes, 0, len);
System.out.println("接受到服务器端的相应 " + s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {

}
}
}).start();

Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String inputStr = scanner.next();
if ("bye".equals(inputStr)) {
break;
}
outputStream.write((clientName + inputStr).getBytes("UTF-8"));
}
outputStream.close();
scanner.close();
}
}

使用线程池的伪BIO

如果使用Socket编程,编写服务端,如果客户端有多个,那么编程模型就是1:N的关系,服务器端很可能会出现线程太多导致CPU消耗殆尽,导致服务卡死或者宕机;所以出现了使用线程池来限制管理线程的伪BIO;

服务端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SupportThreadPoolServer {

public void start() throws IOException {

ServerSocket serverSocket = new ServerSocket(9091);
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 2, 100000, TimeUnit.MILLISECONDS, queue);

for (; ; ) {
Socket socket = serverSocket.accept();
poolExecutor.execute(new ScoketChanil(socket));
}

}

public static void main(String[] args) throws IOException {
new SupportThreadPoolServer().start();
}
}

客户端代码

客户端的代码没变化

ServerSocket阻塞方法

ServerSocket的accept()是一个阻塞方法,如果没有Socket接入会一直阻塞,直到有Socket建立连接。

文件传输问题解决

到了今天我还是没能想清楚怎么传输名字,但是我学会了查资料,把查到的内容学会了,也是自己的。

传输文件名客户端可以用DataOutputStream.writeUTF(“XXX”)将名字写出,服务器端使用DataInputStream.readUTF()得到名字;

自己的Java基础是很功利的看了一段时间视频学习的,清晰的记得学习路线是从最简单的语法到对象,再到集合,线程,IO,Socket编程,最终是泛型和反射;

今天自己也不那么较真了,不会的,虽然网上找到的不是自己的,但是学会了就是自己的。

GitHub登录不了?信任该网站之后再登录。

原文:大专栏  JDK BIO - Socket编程


猜你喜欢

转载自www.cnblogs.com/chinatrump/p/11588861.html