网络地址的表示
- 在计算机网络中,我们通过IP地址来标识、区分网络上的每一台设备。
- IPv4(Internet Protocol Version 4)使用4个字节(32位)来表示一个IP地址。我们通常将每个字节表示成一个十进制数,字节间用"."隔开。
- 在Java语言中,我们使用InetAddress来表示IP地址。
- InetAddress及其它Java网络编程中的常用工具类位于java.net包中,在只用这些类之前,我们需要先导入这个包。
获取本机地址
- 在Java语言中,我们使用InetAddress类的静态方法getLocalHost()来获取本机地址。
- 若IP地址获取失败,则抛出UnknownHostException异常。(用try-catch捕获)
例子:通过getLocalHost()获取本机地址
import java.net.*; public class LocalAddressTest { public static void main(String[] args) { try{ InetAddress localAddress = InetAddress.getLocalHost();//重点 System.out.println(localAddress); } catch(UnknownHostException e){ System.out.println("获取不到本机地址"); } } }
获取互联网主机地址
获取互联网主机地址使用的是InetAddress类的静态方法getByName(String host),其中host可以是主机名(如"www.szu.edu.cn"),也可以是具体的IP地址(如"210.39.3.164")。
通过getByName(String host)获取互联网主机地址
import java.net.*; public class RemoteAddressTest { public static void main(String[] args) { try { InetAddress remoteAddress = InetAddress.getByName("www.szu.edu.cn"); System.out.println(remoteAddress); } catch(UnknownHostException e) { System.out.println("获取不到主机地址"); } } }UDP数据报
端口与数据报套接字
UDP(User Datagram Protocol)数据报协议:一种面向无连接的、不可靠的传输层协议。
-不需要在通信双方间建立连接,而采用”尽最大努力投递“的方式提供通信服务。
-由于其协议开销小、传输延时短、对传输环境要求高,通常用于局域网内不需要高可靠性传输的通信,例如局域网内的视频点播等应用。
- 端口号:UDP数据报协议提供16位的端口号(0-65535)来区分收发数据报的上层应用程序。【最好不使用0-1023的端口】
- 发送方(上层应用程序):除了指定IP地址外,还需要指定数据报的发送端口(源端口)和接收端口(目的端口)。
- 接收方(上层应用程序):监听相应的目的端口,当数据报送达时,上层应用程序就可收到具体的数据报内容。
- 套接字(Socket):UDP协议、IP地址和端口成为上层应用程序之间通信的窗口,即套接字。
- 对UDP协议来说,我们称这个套接字为数据报套接字(DatagramSocket)。
- 数据报套接字有两个常用的构造方法:DatagramSocket()和DatagramSocket(int prot)。通常,前者用于发送数据报,后者用于监听、接收数据报。
- 数据报包裹(DatagramPacket)构造方法
-发送包裹时:使用DatagramPacket(byte[] data, int length, InetAddress remoteAddr, int remotePort),其中remoteAddr和remotePort指明了接收方的地址。(数据、长度、IP地址、端口号)
-接收包裹时:通常只关注包裹的内容,此时常用DatagramPacket(byte[] data, int length)。
例子:发送UDP数据报
import java.net.*; public class SendUDP { public static void main(String[] args) throws Exception { DatagramSocket datagramsocket = null;//窗口 DatagramPacket datagrampacket = null;//包裹 datagramsocket = new DatagramSocket(2345);//把这个Socket的端口号设为2345 String str = "send UDP"; datagrampacket = new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("127.0.0.1"),8000);//创建一个包裹 datagramsocket.send(datagrampacket);//发送 datagramsocket.close(); } }
import java.net.*; public class ReceiveUDP { public static void main(String[] args) throws Exception { DatagramSocket datagramsocket = null; DatagramPacket datagrampacket = null; byte[] data = new byte[1024]; datagramsocket = new DatagramSocket(8000);//要指定端口也是8000(门牌号) datagrampacket = new DatagramPacket(data,data.length); System.out.println("正在等待客户端的数据"); datagramsocket.receive(datagrampacket); System.out.println("收到客户端发来的:"+new String(data)); datagramsocket.close(); } }
要先运行父亲,也就是接收方,再运行儿子,也就是发送方。
TCP连接(重点)连接
- TCP(Transmission Control Protocol)是一种面向连接的传输控制协议,它提供可靠的数据流(data stream)传输服务,是互联网上使用最广泛的传输协议。
- TCP是面向连接的,在通信双方之间进行数据交换之前,双方要建立逻辑连接。连接建立之后,双方的通信就在该连接中进行。
套接字Socket
与数据报套接字类似,TCP也存在套接字的概念,而且,由于TCP比UDP更常见,通常说的套接字(Socket)是指TCP的套接字。
例子:Socket连接到服务器
(如何通过客户端,通过Socket来访问服务器,获得服务器上的信息。主要就是通过套接字来和服务器打交道。)
import java.io.*; import java.net.*; import java.util.*; //作为客户端要创建一个套接字 //并且向这个时间服务器发送一个请求 //希望获得时间服务器上的信息 public class sttest { public static void main(String[] args) { try { Socket s = new Socket("time.nist.gov",13); //第一部分是域名,第二部分是端口号 //有了这两个以后,客户端就可以连接到服务器端 //一旦创建了这个Socket对象,就意味着已经尝试连接到服务器那里去了 try { InputStream is = s.getInputStream(); Scanner in = new Scanner(is); while(in.hasNextLine()) { String line = in.nextLine(); System.out.println(line); } } finally { s.close(); } } catch(IOException e) { e.printStackTrace(); } } }
ServerSocket实现服务器
客户端:使用Socket类来创建socket并连接到服务器端。
服务器端:用来监听socket的类是ServerSocket类。
在Client/Server通信模式中,服务器端需要创建监听特定端口的ServerSocket对象来负责接收客户端的连接请求。
客户端:
import java.net.*; import java.io.*; //客户端 public class DaytimeClient { public static void main(String[] args) { String hostname; if(args.length>0) { hostname = args[0]; } else { hostname = "localhost"; } try {//关键 Socket theSocket = new Socket (hostname,13);//域名,端口号,创建Socket对象 InputStream timeStream = theSocket.getInputStream();//得到输入流 StringBuffer time = new StringBuffer(); int c; while((c = timeStream.read())!=-1)//每次读一个字符,将其加到time里面 time.append((char)c); String timeString = time.toString().trim(); System.out.println("It is "+timeString + " at "+ hostname);//读完后打印 } catch(UnknownHostException e) { System.err.println(e); } catch(IOException e) { System.err.println(e); } } } //首先创建Stocket对象:域名、端口(向服务器端发送请求的过程) //输入流
服务器端
import java.net.*; import java.io.*; //服务端 public class DaytimeServer { public static final int SERVICE_PORT = 13;//端口号,与客户端保持一致 public static void main(String arg[]) { try { ServerSocket server = new ServerSocket(SERVICE_PORT); System.out.println("Daytime service started"); for(;;) { Socket nextClient = server.accept(); //有客户端请求进来之前,这条语句会一直等待,阻塞在这里 //有客户端请求进来后才会接着往下读 System.out.println("Received request from "+ nextClient.getInetAddress()+":"+nextClient.getPort()); OutputStream out = nextClient.getOutputStream();//输出流 PrintStream pout = new PrintStream(out);//将输出流放入PrintStream,好处是可以把对象Date打印出去。 pout.print(new java.util.Date()); out.flush();//清空缓冲区 out.close(); nextClient.close();//关闭Client } } catch(BindException be) { System.err.println("Service already running on port "+SERVICE_PORT); } catch(IOException ioe) { System.err.println("I/O error - "+ioe); } } } //首先创建了ServerSocket对象: //端口号与客户端保持一致,才能连接,从而数据交流 //服务端等到客户端发来请求后才能继续往下走, //从而把信息传给客户端 //一定要先运行服务器端 //一旦运行客户端的程序,客户端就会发送一个请求给服务器端 //服务器端做出反应,把时间信息传递过去 //客户端接收到这个时间信息就会把它打印出来 //服务端关闭当前客户端的连接 //服务器端重新进入等待(append) //如果再运行客户端,服务器端会收到第二条请求,最后输出第二个结果
服务器多线程处理套接字连接
当有多个客户端同时发出请求时,一个服务器可以应对多个请求:
当服务器端收到一个请求时,它就产生一个线程来服务这个客户端。
不管有多少个客户端,服务器端总是能产生新的线程来服务。
例子:使用多线程来处理套接字连接:
import java.io.*; import java.net.*; public class EchoServer { private int port = 8000;//端口号 private ServerSocket serverSocket; public EchoServer() throws IOException { serverSocket = new ServerSocket(port); System.out.println("服务器启动"); } public static void main(String[] args) throws IOException { new EchoServer().service(); } public void service() {//关键 while(true) {//无限等待 Socket socket = null; try { socket = serverSocket.accept(); //一直在等待,只要有客户端请求进来它就会提供服务 Thread workThread = new Thread(new Handler(socket)); //收到请求后,创建一个新的线程 //一个实参传到Thread构造方法里 //里面是一个实现了Runnable接口的类的对象 workThread.start(); } catch(IOException e) { e.printStackTrace(); } } } }
import java.net.*; import java.io.*; public class Handler implements Runnable{ private Socket socket; public Handler(Socket socket) { this.socket = socket; } private PrintWriter getWriter(Socket socket) throws IOException { OutputStream socketOut = socket.getOutputStream(); return new PrintWriter(socketOut,true); } private BufferedReader getReader(Socket socket) throws IOException { InputStream socketIn = socket.getInputStream(); return new BufferedReader(new InputStreamReader(socketIn)); } public String echo(String msg) { return "echo:"+msg; }//过来一个信息就会有回复 public void run() { try { System.out.println("New connection accepted "+socket.getInetAddress()+":"+socket.getPort()); BufferedReader br = getReader(socket); PrintWriter pw = getWriter(socket); String msg = null; while((msg = br.readLine())!=null) { System.out.println(msg); pw.println(echo(msg)); if(msg.equals("bye")) break; } } catch(IOException e) { e.printStackTrace(); }finally { try { if(socket != null) socket.close(); } catch(IOException e) { e.printStackTrace(); } } } }
Socket关闭与半关闭
- 当客户端与服务器端通信结束时,应及时关闭socket,以释放socket占用的资源。Socket的close()方法负责关闭socket。
- Socket的半关闭提供了如下功能:
套接字连接的一端可以终止其输出,但还可以接收来自另一端的数据。例如,向服务器传输数据时,可以关闭一个套接字的输出流来发送给服务器的数据结束,但还可以保持输入流在打开状态。
即服务器端不再输出,但客户端还可以接收。
程序片段:Socket半关闭
Socket socket = new Socket(host,port);//域名,端口号 Scanner in = new Scanner(socket.getInputStream());//输入流放入Scanner PrintWriter write = new PrintWriter(socket.getOutputStream());//输出流 writer.print("finished"); writer.flush();//清空缓冲区 socket.shutdownOutput(); //意思是输出已经完成了,从此不再输出,但仍旧接收输入 while(in.hasNextLine() != null)00.. { String linr = in.nextLine(); } socket.close();
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)