Java网络编程浅谈

xiaoxiao2021-02-28  53

转载请注明出处:http://blog.csdn.net/github_39430101/article/details/77753870

TCP通信

Socket简介

socket通常称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄。其实就是客户端和服务端之间通信的一条管道。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口应用于不同的服务。 应用程序通常通过”套接字”向网络发出请求或者应答网络请求。Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。

获取本地地址和端口号

java.net.Socket为套接字类,其提供了很多方法,其中我们可以通过Socket获取本地的地址以及端口号。

int getLocalPort() 该方法用于获取本地使用的端口号 InetAddress getLocalAddress() 该方法用于获取套接字绑定的本地地址 String getCanonicalHostName() 获取此IP地址的完全限定域名 String getHostAddress() 返回IP地址字符串(以文本表现形式)

Demo

public class TestSocket { public static void main(String[] args) { try { Socket socket = new Socket("localhost",8080); InetAddress add = socket.getLocalAddress(); System.out.println(add.getCanonicalHostName()); System.out.println(add.getHostAddress()); System.out.println(add.getLocalHost()); } catch (Exception e) { e.printStackTrace(); } } }

获取远端地址和端口号

Socket也提供了获取远端的地址以及端口号的方法

int getPort() 该方法用于获取远端使用的端口号 InetAddress.getInetAddress() 该方法用于获取套接字绑定的远端地址

Demo

public void testSocket() throws Exception{ Socket socket = new Socket("localhost",8088); InetAddress inetAdd = socket.getInetAddress(); System.out.println(inetAdd.getCanonicalHostName()); System.out.println(inetAdd.getHostAddress()); System.out.println(socket.getPort()); }

获取网络输入流和网络输出流

通过Socket获取输入流与输出流,这两个方法是使用Socket通讯的关键方法。封装了TCP协议的Socket是基于流进行通讯的。所以我们在创建了双方连接后,只需要获取相应的输入与输出流即可。

InputStream getInputStream() 该方法用于返回此套接字的输入流 OutputStream getOutputStream() 该方法用于返回此套接字的输出流

Demo

public void testSocket() throws Exception { Socket socket = new Socket("localhost",8088); InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); }

Socket通讯模型

Server端ServerSocket监听

java.net.ServerSocket是运行于服务端应用程序中。通常创建ServerSocket需要指定服务端口号,之后监听Socket的连接。监听方法为:

Socket accept() 该方法是一个阻塞方法 //创建ServerSocket并申请服务端口8088 ServerSocket server = new ServerSocket(8088); //方法会产生阻塞,直到某个Socket连接,并返回请求连接的Socket Socket socket = server.accept();

Client端Socket连接

Socket socket = new Socket("localhost",8088);

完整示例(单线程)

服务端创建ServerSocket通过调用ServerSocket的accept方法监听客户端的连接客户端创建Socket并指定服务端的地址以及端口来建立与服务端的连接当服务端accept发现客户端连接后,获取对应客户端的Socket双方通过Socket分别获取对应的输入与输出流进行数据通讯通讯结束后关闭连接 /**************** Server端 *****************/ public class ServerDemo { public static void main(String[] args) { ServerSocket server = null; try { //创建ServerSocket并监听8088端口 server = new ServerSocket(8088); //监听客户端的连接 System.out.println("----等待客户端的连接----"); Socket socket = server.accept(); //客户端连接后,通过该Socket与客户端交互 //获取输入流,用于读取客户端发送过来的消息 InputStream is = socket.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8")); //获取输出流,用于向客户端发送消息 OutputStream os = socket.getOutputStream(); PrintWriter pw = new PrintWriter(new OutputStreamWriter(os,"UTF-8"),true); pw.write("我是服务端,我监听到你了哦"); pw.flush(); //读取客户端发送的消息 String message = reader.readLine(); System.out.println("客户端说:"+message); } catch (Exception e) { e.printStackTrace(); } finally { if(server != null) { try { server.close(); } catch (IOException e) { } } } } } /********** Client端 ************/ public class ClientDemo { public static void main(String[] args) { Socket socket = null; try { socket = new Socket("localhost",8088); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8")); //获取输出流,用于向服务端发送消息 OutputStream os = socket.getOutputStream(); OutputStreamWriter ow = new OutputStreamWriter(os,"UTF-8"); PrintWriter pw = new PrintWriter(ow,true); //向服务端发送一个字符串 pw.println("你好服务器"); pw.flush(); String message = br.readLine(); System.out.println("服务器说:"+message); } catch (Exception e) { e.printStackTrace(); } finally { try { if(socket != null) { socket.close(); } } catch (Exception e2) { e2.printStackTrace(); } } } }

先启动服务器,后连接。

Server端多线程模型

我们已经知道如何使用ServerSocket与Socket进行通讯了,但是只能点对点,一个服务端对一个客户端。若我们想让一个服务端可以同时支持多个客户端应该怎么做呢?这时我们需要分析之前的代码。我们可以看到,当服务端的ServerSocket通过accept方法监听到一个客户端连接后,就获取该Socket并与该客户端通过流进行双方的通讯了,这里的问题在于,只有不断的调用accept方法,我们才能监听到不同客户端的连接。但是若我们循环监听客户端的连接,又无暇顾及与连接上的客户端交互,这时我们需要做的事情就是并发。我们可以创建一个线程类ClientHandler,并将于客户端交互的工作全部委托线程来处理。这样我们就可以在当一个客户端连接后,启动一个线程与客户端交互,而我们也可以循环监听客户端的连接了。

/*************** Server端应用程序 **************/ package com.code.socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class ServerDemo { public static void main(String[] args){ ServerSocket server = null; try{ server = new ServerSocket(8088); while(true){ //循环监听客户端的连接 Socket socket = server.accept(); //当一个客户端连接后,启动线程来处理该客户端的交互 new ClientHandler(socket).start(); } } catch (Exception e){ e.printStackTrace(); } finally { if(server != null){ try{ server.close(); } catch (IOException e){ } } } } } /* * 线程类 该线程的作用是并发与客户端进行交互 */ class ClientHandler extends Thread{ private Socket socket; public ClientHandler(Socket socket) { this.socket = socket; } public void run() { try { //获取输入流,用于读取客户端发送过来的消息 InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8")); String info = null; while(null != (info=br.readLine())) { System.out.println("客户端说:"+info); } //获取输出流,用于向客户端发送给消息 OutputStream os = socket.getOutputStream(); PrintWriter pw = new PrintWriter(new OutputStreamWriter(os,"UTF-8"),true); pw.println("你好客户端"); pw.flush(); } catch (Exception e) { e.printStackTrace(); } } }

UDP通信

创建接受包

DatagramPacket:UDP数据报给予IP建立的,每台主机有65536个端口号可以使用。数据报中字节数限制为65536-8.包含8字节的头信息。

DatagramPacket(byte[] buf,int length)

将数据包中Length长的数据装进buf数组

DatagramPacket(byte[] buf,int offset,int length)

将数据包中从offset开始,length长的数据装进buf数组

创建发送包
DatagramPacket(byte[] buf,int length,InetAddress clientAddress,int clientPort)

从buf数组中,取出length长的数据创建爱你数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。

DatagramPacket(byte[] buf,int offset,int length,InetAddress clientAddress,int clientPort)

从Buf数组中,取出Offset开始的、Length长的数据创建数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。

DatagramSocket

服务端接收

DatagramSocket用于接收和发送UDP的Socket实例

DatagramSocket(int port)

创建实例,并固定监听port端口的报文。通常用于服务端。 其中方法:

receive(DatagramPacket d)

接收数据报文到d中。receive方法产生阻塞。会一直等待直到有数据被读取到。

客户端发发送

无参的构造方法DatagramSocket()通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。程序会让操作体统分配一个可用的端口。

send(DatagramPacket dp)

该方法用于发送报文dp到目的地 代码如下:

/***************** Server端 *****************/ import java.net.DatagramPacket; import java.net.DatagramSocket; public class UdpServer { public static void main(String[] args) { DatagramSocket socket = null; try { socket = new DatagramSocket(8088); byte[] data = new byte[1024]; DatagramPacket dp = new DatagramPacket(data,data.length); socket.receive(dp);//会产生阻塞,读取发送过来的数据 String str = new String(dp.getData(),0,dp.getLength());//从包中取数据 System.out.println(str); } catch (Exception e) { e.printStackTrace(); } finally { if(socket != null) { socket.close(); } } } } /*********************** 客户端 **********************/ import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UdpClient { public static void main(String[] args) { DatagramSocket socket = null; try { socket = new DatagramSocket(); //创建socket byte[] data = "你好".getBytes(); //创建爱你发送包 DatagramPacket dp = new DatagramPacket(data,data.length,InetAddress.getByName("localhost"),8088); socket.send(dp); //发送数据 } catch (Exception e) { e.printStackTrace(); } finally { if(socket != null) { socket.close(); } } } }
转载请注明原文地址: https://www.6miu.com/read-55956.html

最新回复(0)