Java网络编程

Java网络编程,第1张

Java网络编程 网络地址InetAddress

网络地址的表示

  1. 在计算机网络中,我们通过IP地址来标识、区分网络上的每一台设备。
  2. IPv4(Internet Protocol Version 4)使用4个字节(32位)来表示一个IP地址。我们通常将每个字节表示成一个十进制数,字节间用"."隔开。
  3. 在Java语言中,我们使用InetAddress来表示IP地址。
  4. InetAddress及其它Java网络编程中的常用工具类位于java.net包中,在只用这些类之前,我们需要先导入这个包。

获取本机地址

  1. 在Java语言中,我们使用InetAddress类的静态方法getLocalHost()来获取本机地址。
  2. 若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)数据报协议:一种面向无连接的、不可靠的传输层协议。
-不需要在通信双方间建立连接,而采用”尽最大努力投递“的方式提供通信服务。
-由于其协议开销小、传输延时短、对传输环境要求高,通常用于局域网内不需要高可靠性传输的通信,例如局域网内的视频点播等应用。

  1. 端口号:UDP数据报协议提供16位的端口号(0-65535)来区分收发数据报的上层应用程序。【最好不使用0-1023的端口】
  2. 发送方(上层应用程序):除了指定IP地址外,还需要指定数据报的发送端口(源端口)和接收端口(目的端口)。
  3. 接收方(上层应用程序):监听相应的目的端口,当数据报送达时,上层应用程序就可收到具体的数据报内容。
  4. 套接字(Socket):UDP协议、IP地址和端口成为上层应用程序之间通信的窗口,即套接字。
  5. 对UDP协议来说,我们称这个套接字为数据报套接字(DatagramSocket)。
  6. 数据报套接字有两个常用的构造方法:DatagramSocket()和DatagramSocket(int prot)。通常,前者用于发送数据报,后者用于监听、接收数据报。
  7. 数据报包裹(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连接(重点)

连接

  1. TCP(Transmission Control Protocol)是一种面向连接的传输控制协议,它提供可靠的数据流(data stream)传输服务,是互联网上使用最广泛的传输协议
  2. 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关闭与半关闭

  1. 当客户端与服务器端通信结束时,应及时关闭socket,以释放socket占用的资源。Socket的close()方法负责关闭socket。
  2. 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();

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5687068.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存