转载请注明出处:http://blog.csdn.net/github_39430101/article/details/77753870
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(); }java.net.ServerSocket是运行于服务端应用程序中。通常创建ServerSocket需要指定服务端口号,之后监听Socket的连接。监听方法为:
Socket accept() 该方法是一个阻塞方法 //创建ServerSocket并申请服务端口8088 ServerSocket server = new ServerSocket(8088); //方法会产生阻塞,直到某个Socket连接,并返回请求连接的Socket Socket socket = server.accept();先启动服务器,后连接。
我们已经知道如何使用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(); } } }DatagramPacket:UDP数据报给予IP建立的,每台主机有65536个端口号可以使用。数据报中字节数限制为65536-8.包含8字节的头信息。
DatagramPacket(byte[] buf,int length)将数据包中Length长的数据装进buf数组
DatagramPacket(byte[] buf,int offset,int length)将数据包中从offset开始,length长的数据装进buf数组
从buf数组中,取出length长的数据创建爱你数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。
DatagramPacket(byte[] buf,int offset,int length,InetAddress clientAddress,int clientPort)从Buf数组中,取出Offset开始的、Length长的数据创建数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。
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(); } } } }