Java--网络编程TCP和UDP
计算机网络
,就是把分布在不同区域的计算机用通信线路连接在一起、可以方便地互相传递信息,共享资源。网络编程
,就是直接或间接地通过网络协议与其他计算机进行通讯
。
网络编程中有两个重要问题:
- 如何准确地定位网络上一台或多台主机。
- 找到主机后如何可靠高效地进行数据传输。
基础术语
- IP地址
网络中每台主机都必须有一个
唯一的IP地址
,IP地址是一个逻辑地址。
英特网上的IP地址具有全球唯一性。
32位,四个字节,常用点分十进制的格式表示。例如:113.45.153.190
- 协议
为进行网络中的数据交换(通信)而建立的规则、标准或约定。(=语义+语法+规则)。
不同层具有各自不同的协议。
- 端口
在互联网上传输的数据都包含有用来识别目的地的IP地址和端口号。
IP地址用来标识网络上的计算机,而端口号用来指明该计算机上的应用程序。
端口用一个整数型标识符来表示,即端口号。
端口使用一个16位的数字来表示,它的范围是0~65535,1024以下的为保留端口。
- 数据封装
一台计算机要发送数据到另一台计算机,数据首先必须打包,打包的过程称为封装。
封装就是在数据前面加上特定的协议头部。
在每一层传递信息的过程中都会进行一次封装,服务端收到信息后然后进行一层一层的解封装最终得到数据。
ISO七层模型
网络体系结构解决异质性问题采用的是分层的方法——把复杂的网络互联问题划分为若干个较小的、单一的问题,在不同层上予以解决,各层之间是严格的单向依赖。
- OSI(Open System Interconnection)参考模型7层:
- OSI各层所使用的协议:
应用层:Telnet、FTP、HTTP、DNS、SMTP、POP3
传输层:TCP、UDP
网络层:IP、ICMP、IGMP
详细讲解可查看博客:http://blog.csdn.net/yaopeng_2005/article/details/7064869
TCP/UDP
套接字Socket
,是连接运行在网络上的两个程序间的双向通讯的端点。网络通信其实就是Socket间的通信。即,数据在两个Socket间通过IO传输。
UDP
- UDP支持更简单的、快速的、点对点的数据报模式
将数据及源和目的封装成数据包中
不需要建立连接
。
无连接,即是不可靠协议、速度快。
协议并不保证数据报是否能正确地到达目的地。
每个数据报的大小在限制在64KB内。
- 实例一 、简单的接收和发送数据DEMO
发送数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 通过udp协议发送数据
* Created by xiaoxiaomo on 2016/4/12.
*
*/
public class UdpSendSimpleDemo {
/**
* 1:获取socket对象
* 2:对数据进行封包
* 3:通过socket对象把数据包发送出去
* 4:把连接关闭
* @param args
*/
public static void main(String[] args) throws IOException {
//1. 获取socket对象
DatagramSocket ds = new DatagramSocket();
//2. 封包数据
String sendMsg = "UDP ......你好!" ;
/**
* @param buf 字节流数据包
* @param length 流数据包长度 eg,InetAddress.getByName("192.168.3.102")
* @param address 发送地址
* @param port 端口号
*/
DatagramPacket p =
new DatagramPacket(sendMsg.getBytes(),sendMsg.getBytes().length,InetAddress.getLocalHost(),3000);
//3. 发送数据包
ds.send(p);
//4. 关闭连接
ds.close();
}
}接送数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* UDP接收端代码
* Created by xiaoxiaomo on 2016/4/12.
*
*/
public class UdpReceiveSimpleDemo {
/**
* 1:获取socket连接(需要指定监听的端口)
* 2:通过receive方法接收数据包
* 3:解包,获取数据包中的内容
* 4:关闭连接
* @param args
* @throws Exception
*/
public static void main(String[] args) throws IOException {
//1:获取连接
DatagramSocket ds = new DatagramSocket(3000);;
//2:接收数据
byte[] buf = new byte[1024];
DatagramPacket p = new DatagramPacket(buf, 0, buf.length);
ds.receive(p);
//3:获取数据
System.out.println( "信息来源:"+p.getAddress().getHostAddress() );
System.out.println( "收到数据:" + new String( p.getData(),0,p.getLength()) );
//4:关闭连接
ds.close();
}
}运行结果
1
2
3
4如果先运行UdpSendSimpleDemo没有任何信息
如果先运行UdpReceiveSimpleDemo会输出:
信息来源:192.168.3.102
收到数据:UDP ......你好!
- 实例二 、相对复杂的实例
发送数据后,如果不成功会继续发送4次,都不成功则表示失败。
接收端收到信息后,会返回消息告诉发送者已成功,发送者收到数据会进行相应的校验。
发送数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.*;
/**
* 通过udp协议发送数据
* Created by xiaoxiaomo on 2016/4/12.
*
*/
public class UdpSendDemo {
private static final int TIMEOUT = 3000; //设置接收数据的超时时间
private static final int MAXNUM = 5; //设置重发数据的最多次数
/**
* 1:获取socket对象
* 2:对数据进行封包
* 3:通过socket对象把数据包发送出去
* 4:把连接关闭
* @param args
*/
public static void main(String[] args) {
//1. 获取socket对象
DatagramSocket ds = null;
try {
ds = new DatagramSocket(9000); //如果只是用于发送不填写端口
} catch (SocketException e) {
System.out.println("创建一个socket对象失败!");
}
//2. 封包数据
String sendMsg = "UDP ......你好!" ;
DatagramPacket p = null;
try {
p = new DatagramPacket(sendMsg.getBytes(),sendMsg.getBytes().length,InetAddress.getLocalHost(),3000);
} catch (UnknownHostException e) {
System.out.println("找不到该hosts所对应的地址!");
}
//3. 发送数据包
boolean isReceived = false; //是否接收到数据的标志位
int tries = 0; //重发数据的次数
try {
ds.setSoTimeout(TIMEOUT); //设置超时时间
//直到接收到数据,或者重发次数达到预定值,则退出循环
while(!isReceived && tries<MAXNUM) {
ds.send(p); //发送数据
try{
//定义用来接收数据的DatagramPacket实例
DatagramPacket dp_receive = new DatagramPacket(new byte[1024], 1024);
//接收从服务端发送回来的数据
ds.receive(dp_receive);
//如果接收到的数据不是来自目标地址,则抛出异常
if(!dp_receive.getAddress().equals( InetAddress.getLocalHost() )){
throw new IOException("地址出错");
}
//如果接收到数据。则将receivedResponse标志位改为true,从而退出循环
isReceived = true;
}catch(InterruptedIOException e){
//如果接收数据时阻塞超时,重发并减少一次重发的次数
tries += 1;
System.out.println("Time out,次数:" + tries );
}
}
} catch (IOException e) {
System.out.println("发送数据失败!");
}
if(isReceived){
//成功
System.out.println("发送成功!");
}else{
//失败
System.out.println("重发信息"+tries+"次后,失败!");
}
//4. 关闭连接
if( ds != null ){
ds.close();
}
}
}接收数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* UDP接收端代码
* Created by xiaoxiaomo on 2016/4/12.
*
*/
public class UdpReceiveDemo {
/**
* 1:获取socket连接(需要指定监听的端口)
* 2:通过receive方法接收数据包
* 3:解包,获取数据包中的内容
* 4:关闭连接
* @param args
* @throws Exception
*/
public static void main(String[] args) {
//1:获取连接
DatagramSocket ds = null;
try {
//在3000端口监听接收到的数据
ds = new DatagramSocket(3000);
} catch (SocketException e) {
//例如,该端口已被占用
//java.net.BindException: Address already in use: Cannot bind
System.out.println("创建一个socket对象失败!"+e.getMessage());
}
//这些让它一直接收数据
while( ds != null ){
//2:接收数据
byte[] buf = new byte[1024];
DatagramPacket p = new DatagramPacket(buf, 0, buf.length);
try {
ds.receive(p);
} catch (IOException e) {
//例如,ds为空等
System.out.println("接收数据失败!");
}
//3:获取数据
System.out.println( "信息来源:"+p.getAddress().getHostAddress() );
System.out.println( "收到数据:" + new String( p.getData(),0,p.getLength()) );
//通知9000端口的客户端数据接收成功
//数据发动到客户端的
DatagramPacket dp_reply= new DatagramPacket(p.getData() ,p.getData().length,p.getAddress(),9000);
try {
ds.send(dp_reply);
} catch (IOException e) {
e.printStackTrace();
}
//由于dp_reply在接收了数据之后,其内部消息长度值会变为实际接收的消息的字节数,
//所以这里要将dp_reply的内部消息长度重新置为1024
dp_reply.setLength(1024);
}
//4:关闭连接
//ds.close();
}
}
- 运行结果
1 | UdpSendDemo先运行,如中途UdpReceiveDemo还未启动则运行结果: |
TCP
- TCP用于网络的可靠的流式输入/输出,HTTP、FTP、Telnet等应用都需要这种可靠的通信通道。、
两个socket之间
必须建立连接
,形成传输数据的通道
在连接中进行大数据量传输
通过三次握手完成连接 是可靠协议、效率会稍低
- 事例一、简单的TCP实例
TCP发送端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30import java.io.OutputStream;
import java.net.Socket;
/**
* TCP发送端
* Created by xiaoxiaomo on 2016/4/12.
*/
public class TcpSendSimpleDemo {
/**
* 1:创建socker客户端,需要连接到接收端
* 2:获取这个socket的输出流
* 3:通过输出流给其他服务器发送数据
* 4:关键连接
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//1:获取socker对象
Socket socket = new Socket("192.168.3.102",6000);
//2:获取输出流
OutputStream outputStream = socket.getOutputStream();
//3:通过输出流写数据
outputStream.write("TCP ... 你好!".getBytes());
//4:关闭连接
socket.close();
}
}TCP接收端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* TCP接收端
* Created by xiaoxiaomo on 2016/4/12.
*/
public class TcpReceiveSimpleDemo {
/**
* 1:创建一个socket服务端,需要监听指定端口
* 2:通过这个服务端对象可以获取到给指定端口发送数据的socket客户端对象
* 3:通过socket对象获取具体的读取流
* 4:通过读取流获取数据
* 5:关闭连接
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//1:获取ServerSocket
ServerSocket serverSocket = new ServerSocket(6000);
//2:获取客户端的socket对象
//是一个阻塞方法,获取socket客户端对象
Socket socket = serverSocket.accept();
//3:获取socket的输入流
InputStream in = socket.getInputStream();
byte[] bytes = new byte[1024];
//4:读取数据
int read = in.read(bytes);
System.out.println(new String(bytes, 0, read));
//5:关闭连接
socket.close();
serverSocket.close();
}
}
- InetAddr类讲解:Java-网络编程InetAddr和URL