1、概述



  网络编程,又称为Socket编程,即网络通信的两端都是Socket的对象,Socket底层是IO流的传输,Socket对象可以自动完成网络底层模型中的工作,比如IP、传输协议、端口号的封装和解封装,数据传输等,它是网络服务向开发者提供的一种机制,可以让开发者避免直接面对复杂的网络模型和流程,而通过简单的Sockec操作完成网络数据的传输。


  根据传输层中不同的传输协议:TCP/IP协议和UDP协议,数据的传输方式不同,他们之间的差异主要如下:

1、TCP协议通过三次握手,建立稳定的数据通道;而UDP则面向无连接的通信,通信前不需要建立连接。

2、TCP一次可以进行大量数据的传输;而UDP则一次传输的数据大小不能超过64KB。

3、TCP是可靠协议,数据可以无差错、有序的传输;而UDP是不可靠的传输协议,数据会出现丢失。

4、TCP因为需要事先建立连接,所以数据的传输效率比较低;而UDP传输效率更高。

不同的传输协议,导致了不同的Socket的编程方式,在Java API中,它们是不同的类,操作方式也存在很大的差异。


2、Java API 中UDP编程知识点。


1、Java API为UDP协议的提供的Socket类是DatagramSocket。

2、通信两端的Soekct对象,都是DatagramSocket对象。

3、DatagramSocket可以通过构造函数,绑定一个0-65536之间的端口号,指定Socket服务监听该端口号。

4、通信要素(IP地址、端口号)及传输数据都封装在DatagramPacket(数据报包)对象中,而不是DatagramSocket对象中。

5、DatagramSocket提供void send(DatagramPacket p)void receive(DatagramPacket p)方法,用来发送传入的数据包和指定数据包来装载接收数据。

6、创建发送端数据包时,需要分别接收字节数组、IP地址、端口号,而创建接收数据包时,则只需指定一个足够长的字节数组。

7、DatagramSocket调用close()方法关闭底层资源。


3、创建一个简单的UDP通讯程序的步骤。


   发送端:

1、通过无参构造函数创建一个DatagramSocket对象。如果Socket服务需要接收数据,则传入一个int类型的端口号,使Socket服务监听该端口号。

2、通过构造函数DatagramPacket(byte[] buf,int length,InetAddress address,int port)创建需要发送的数据包DatagramPacket对象,该构造函数接收4个参数,分别是一个字节数组(传输数据)、包长度(一般为数组长度)、IP对象(目的地址)、端口号(目的端口,范围:0-65536)。

3、通过DatagramSocket对象调用void send(DatagramPacket p)方法,把第2步中创建的包对象传入该方法的参数中。

4、通过DatagramSocket对象调用close()关闭资源。


代码示例:

package com.example.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPSender {

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

		//1.创建Socket服务对象,绑定端口号
		DatagramSocket ds = new DatagramSocket(8000);
		
		//2.创建数据包对象
		//传输一段文本信息
		String text = "Hello,UDP Receiver!";
		DatagramPacket dp = new DatagramPacket(text.getBytes(),text.getBytes().length,InetAddress.getLocalHost(),3000);
		
		//3.调用socket服务的send()方法,发送数据包
		ds.send(dp);
		
		//4.关闭资源
		ds.close();
	}

}




   接收端:

1、通过DatagramSocket(int port)构造函数创建一个DatagramSocket对象,该方法传入一个端口号,表示该Socket服务监听该端口号。

2、通过构造函数DatagramPacket(byte[] buf,int length)创建一个DatagramPacket对象,用来接收长度为length的数据包。当长度小于接收的数据包时,数据会进行截取。

3、通过DatagramSocket对象调用void receive(DatagramPacket p)方法,把第2步中创建的包对象传入该方法参数中,用来装载接收到的数据,当该方法返回时,该数据包对象包含了发送端的数据、IP地址、发送端端口号。该方法在未接收到数据前,处于阻塞状态,程序挂起。

4、如果第3步receive()方法返回,则参数中指定的DatagramPacket对象成功装载了发送端的数据,接着调用API所提供的方法,如byte[] getData()InetAddress getAddress()int getPort()等,返回接收到的具体的信息,并打印出来。需要注意的是在DatagramPacket对象接收了发送端数据后,包的长度将由初始化时的长度变为接收到数据的字节长度,可以通过int getLength()方法返回。其中端口号信息是指定发送socket服务绑定的端口号,而不是发送端数据包中指定的接收端的端口号,如果没有绑定,则随机分配一个可用的端口号。

4、通过DatagramSocket对象调用close()关闭资源。


代码示例:

package com.example.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPReceiver {

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

		//1.创建Socket服务对象,绑定端口号
		DatagramSocket ds = new DatagramSocket(3000);
		
		//2.创建一个数据包,用来接收发送端的信息。
		
		/*
		 * 定义一个足够长的字节数据,用来装载数据,如果你担心发送端的数据过大,你可以把字节数组长度设为65507,这是发送数据最大长度。
		 * 因为UDP每次最多只能发送64KB,长度最大为64×1024=65535,它还包含20字节的IP首部和8个字节的UDP首部,所以数据长度只能为65507个字节。
		 */
		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf,buf.length);
		
		/*
		 * 3.调用socket服务的receive()方法,传入第2步创建的数据包,该方法是阻塞式。
		 * 如果方法返回,则参数传入的数据包会装载接收到的数据,包括发送数据、IP地址、客户端端口号等。
		 */
		ds.receive(dp);
		
	
		 //4.调用第二步中数据包对象的方法,返回接收到的信息,如数据、IP地址、端口等,并打印出来。
		 
		InetAddress ip = dp.getAddress();
		//端口是指发送端绑定的端口,而不是数据包的端口号,如果发送端没有指定,则socket服务会任意绑定到一个可用端口上
		int port = dp.getPort();
		byte[] receText = dp.getData();
		//打印
		System.out.println(ip.getHostAddress()+":"+port+","+new String(receText,0,dp.getLength()));
		
		//5.关闭资源
		ds.close();
	}

}

结果:

java tcp udp编程不同 java socket tcp udp_端口号


4、代码升级。



1、发送端接收键盘输入并发送,而不是固定文本信息。


发送端:

package com.example.network;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPSender2 {

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

		//1.创建Socket服务对象,绑定端口号
		DatagramSocket ds = new DatagramSocket(8000);

		//键盘输入流
		InputStream in = System.in ;
		//装饰
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String line ;
		while((line = br.readLine()) != null){//阻塞式,在键盘按回车时读取信息,并且不会结束,继续以阻塞状态运行。
			//2.创建数据包对象
			DatagramPacket dp = new DatagramPacket(line.getBytes(),line.getBytes().length,InetAddress.getLocalHost(),3000);					
			//3.调用socket服务的send()方法,发送数据包
			ds.send(dp);
			//定义一个结束标志。
			if("over".equals(line)){
				break;
			}
		}

		//4.关闭资源
		ds.close();
	}

}



接收端:

package com.example.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPReceiver2 {

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

		//1.创建Socket服务对象,绑定端口号
		DatagramSocket ds = new DatagramSocket(3000);
		
		//2.创建一个数据包,用来接收发送端的信息。
		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf,buf.length);
		
		//一直等待接收数据
		while(true){
			ds.receive(dp);
			
			InetAddress ip = dp.getAddress();
			int port = dp.getPort();
			byte[] receText = dp.getData();
			
			//打印
			System.out.println(ip.getHostAddress()+":"+port+","+new String(receText,0,dp.getLength()));
		}
	}

}

结果:

java tcp udp编程不同 java socket tcp udp_java_02


2、使用多线程,实现在一个DOS窗口下,同时进行发送和接收。


发送端:

package com.example.network;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Sender implements Runnable {

	private DatagramSocket ds;
	Sender(DatagramSocket ds){
		this.ds = ds;
	}
	@Override
	public void run() {
		try {
			InputStream in = System.in ;
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String line ;
			while((line = br.readLine()) != null){
				DatagramPacket dp = new DatagramPacket(line.getBytes(),line.getBytes().length,InetAddress.getLocalHost(),3000);					
				ds.send(dp);
				if("over".equals(line)){
					break;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			ds.close();		
		}
	}
}




接收端:

package com.example.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Receiver implements Runnable  {

	private DatagramSocket ds;
	Receiver(DatagramSocket ds){
		this.ds = ds;
	}
	@Override
	public void run() {
			byte[] buf = new byte[1024];
			DatagramPacket dp = new DatagramPacket(buf,buf.length);
			while(true){
				try {
					ds.receive(dp);
					InetAddress ip = dp.getAddress();
					int port = dp.getPort();
					byte[] receText = dp.getData();
					System.out.println(ip.getHostAddress()+":"+port+","+new String(receText,0,dp.getLength()));
				} catch (IOException e) {
					e.printStackTrace();
				}
			}	
	}
	
}


主程序:

package com.example.network;

import java.net.DatagramSocket;
import java.net.SocketException;

public class ChatDemo {

	public static void main(String[] args) throws SocketException {
		DatagramSocket sendSocket = new DatagramSocket(8000);
		DatagramSocket receSocekt = new DatagramSocket(3000);
		
		new Thread(new Sender(sendSocket)).start();
		new Thread(new Receiver(receSocekt)).start();

	}

}

结果:

java tcp udp编程不同 java socket tcp udp_.net_03


3、发送广播信息,而不是仅仅向一个主机发送信息。

  只需把发送端数据包中发送的IP的地址,指定为当前网段的广播地址,即最后一位为255,则发送端发送数据包,局域网中所有的计算机都会接收到该数据。


代码示例:

DatagramPacket dp = new DatagramPacket(line.getBytes(),line.getBytes().length,InetAddress.getByName("192.168.1.255"),3000);





优秀BLOG学习(未完成):。