代码改变世界

Java Socket编程(二)Socket基础

2012-08-04 21:51  java线程例子  阅读(526)  评论(0编辑  收藏  举报

二、Socket基础

1.地址的获得
	public static void main(String[] args) {

		try {
			Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
			while (interfaces.hasMoreElements()) {
				NetworkInterface iface = interfaces.nextElement();
				System.out.println("Interface: " + iface.getName());
				
				Enumeration<InetAddress> addrList = iface.getInetAddresses();
				if (!addrList.hasMoreElements())
					System.out.println("No address");
				
				while (addrList.hasMoreElements()) {
					InetAddress address = addrList.nextElement();
					System.out.println("Address: " + address.getHostAddress());
				}
			}
			
		} catch (SocketException e) {
			e.printStackTrace();
		}
		
	}

2.TCP实例程序

要注意一点,虽然在Client端只用了一个write()方法发送字符串,服务器端也可能从
多个块中接受该信息。即使回馈字符串在服务器返回时存于一个块中,也可能被TCP
协议分割成多个部分。

TCPEchoClientTest.java
	public static void main(String[] args) throws IOException {

		String server = args[0];
		byte[] data = args[1].getBytes();
		int port = 7;
		
		Socket socket = new Socket(server, port);
		System.out.println("Connected to server...");
		
		InputStream in = socket.getInputStream();
		OutputStream out = socket.getOutputStream();
		
		out.write(data);
		
		int totalBytesRcvd = 0;
		int bytesRcvd;
		while (totalBytesRcvd < data.length) {
			if ((bytesRcvd = in.read(data, totalBytesRcvd, 
					data.length - totalBytesRcvd)) == -1)
				throw new SocketException("Connection closed");
			totalBytesRcvd += bytesRcvd;
		}
		
		System.out.println("Received: " + new String(data));
		
		socket.close();
	}

TCPEchoServerTest.java
	private static final int BUFSIZE = 32;
	
	public static void main(String[] args) throws IOException {

		ServerSocket serverSocket = new ServerSocket(7);
		
		int recvMsgSize;
		byte[] receiveBuf = new byte[BUFSIZE];
		while (true) {
			Socket socket = serverSocket.accept();
			System.out.println("Handling client " +
					" from remote " + socket.getRemoteSocketAddress() + 
					" at local " + socket.getLocalSocketAddress());
			
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			
			while ((recvMsgSize = in.read(receiveBuf)) != -1) {
				out.write(receiveBuf, 0, recvMsgSize);
			}
			socket.close();
		}
		
	}
注意new Socket时指定的是远端服务器监听的端口号而没有指定本地端口,因此将
采用默认地址和可用的端口号。在我的机器上Client端口是4593,连接到服务器的
端口7。


3.UDP实例程序

为什么使用UDP协议?如果应用程序只交换少量的数据,TCP连接的建立阶段就至少
要传输其两倍的信息量(还有两倍的往返时间)。

UDPEchoClientTest.java
	public static void main(String[] args) throws IOException {

		InetAddress serverAddress = InetAddress.getByName(args[0]);
		byte[] bytesToSend = args[1].getBytes();
		
		DatagramSocket socket = new DatagramSocket();
		socket.setSoTimeout(3000);
		
		DatagramPacket sendPacket = new DatagramPacket(
			bytesToSend, bytesToSend.length, serverAddress, 7);
		
		DatagramPacket receivePacket = new DatagramPacket(
			new byte[bytesToSend.length], bytesToSend.length);
		
		// Packets may be lost, so we have to keep trying
		int tries = 0;
		boolean receivedResponse = false;
		do {
			socket.send(sendPacket);
			try {
				socket.receive(receivePacket);
				if (!receivePacket.getAddress().equals(serverAddress))
					throw new IOException("Receive from unknown source");
				receivedResponse = true;
			} 
			catch (IOException e) {
				tries++;
				System.out.println("Timeout, try again");
			}
		} while (!receivedResponse && tries < 5);
		
		if (receivedResponse)
			System.out.println("Received: " + new String(receivePacket.getData()));
		else
			System.out.println("No response");
		
		socket.close();
	}

UDPEchoServerTest.java
	private static final int ECHOMAX = 255; 
	
	public static void main(String[] args) throws IOException {

		DatagramSocket socket = new DatagramSocket(7);
		DatagramPacket packet = new DatagramPacket(new byte[ECHOMAX], ECHOMAX);
		
		while (true) {
			socket.receive(packet);
			System.out.println("Handling client at " + packet.getAddress());
			
			socket.send(packet);
			packet.setLength(ECHOMAX);
		}
		
	}
通过这个例子与之前TCP的实例进行比较,有如下区别:

A.DatagramSocket在创建时不需要指定目的地址,因为UDP不需要建立连接,每个
数据报文都可以发送或接收于不同的目的地址。

B.如果像TCP一样在read()上阻塞等待,将可能永远阻塞在那里,因为UDP协议只是
简单地扩展了IP协议,UDP报文可能丢失掉。所以一定要设置阻塞等待的超时时间。

C.UDP协议保留了消息的边界信息,每次receive()调用最多只能接收一次send()方法
调用所发送的数据。

D.一个UDP报文DatagramPacket能传输的最大数据是65507字节,超出部分的字节将
自动被丢弃,而且对接收程序也没有任何的提示。因此缓存数组可以设置成65000字节
左右是安全的。

E.如果反复使用同一个DatagramPacket实例调用receive()方法,每次调用前都必须显式
地将消息的内部长度重置为缓存区的实际长度。