网络编程基础
学习网络编程的原因
网络编程主要是实现计算机与计算机之间的数据传输问题,网络编程要与网页编程概念区分开来,Java一般不用来做桌面应用软件,PC机基本不用网络编程,但是如果以后要学习的是安卓方向,那么就会用到网络编程知识。那么为什么又要学习网络编程呢?因为在Java SE的小项目中,设计到网络编程、GUI编程的等都集合在一起的项目才是练手的最好项目,所以我觉得还是要好好学习网络编程。在网络编程中应该学习的内容是:分布在不同地域的计算机通过外部设备实现消息、数据、资源共享。
网络通讯的三要素
- IP
地址
- 端口
- 找到双方通信软件的方式,不同的软件通过不同的端口对相应的软件进行监控
- 协议
- 计算机与计算机之间通讯需要外界设备,比如路由器,协议规定了路由双方必须采用相同的协议进行解析,否则根本不能正确的发送消息。协议用来规范通信双方发送的数据帧以及编码解码的规定。
IP地址
IP地址的本质就是一个由32位的二进制数组成的数据,但是为了便于人们读取,将其中的8位作为一个单元,切分成了4份,就形成了现在的方式,比如:192.168.101.44等,每一个切分后的块可表示的范围是0-255。
IP地址等于网络号+主机号构成,其中子网掩码中未255的部分全部都为网络号,IP地址可以分为三类:
- A类地址: 一个网络号+三个主机号 2^24次方台机器 政府机关在用
- B类地址: 两个网络号+两个主机号 2^16次方台机器 学校、银行等在用
- C类地址 三个网络号+一个主机号 2^8次方台几区 私人使用
在Java中使用使用了一个类来描述IP地址。在java.NET.InetAddress类中,可以用来描述一个IP地址,下面的代码说明了该类的基本用法。
package com.jpzhutech.inetaddress;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class TestInetAddress {
public static void main(String[] args) {
InetAddress inetAddress = null;
try {
inetAddress = InetAddress.getLocalHost();
String hostName = inetAddress.getHostName();
String hostAddress = inetAddress.getHostAddress();
System.out.println(hostAddress);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
}
- 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
- 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
端口
端口在Java中并没有使用类来描述,因为端口号仅仅是一个标识符,端口号从0-65535的范围,其它的无效,但是我们都能用0-65535吗?回答是不能,0-1023系统绑定了一些服务,不能用,如果用会报错。1024-49151系统绑定了一些松散的服务,松散的含义是有一些端口可能没被占用,有些被占用了,也可以使用这里的端口,如果发生冲突换一个端口就好了。1024-65535我们可以使用,如果发生冲突就换一个其他的端口号。49152-65535动态或者私有的端口,我们可以随便用,在使用之前使用cports查看你想使用的端口是否有端口占用。
协议
- UDP通讯协议
- 将数据封装成为数据包,不需要建立连接。
- 每个包的大小限制在64K中。
- 因为无连接所以不可靠,不可靠指的是可能发生数据包的丢失
- 因为不需要建立连接,所有速度快
- User Datagram Protocol的简写
- UDP通信是不分客户端和服务端的,只分接收端和发送端
- 例如:对讲机、游戏…
- TCP通讯协议
- 面向连接,有特有的通道
- 在连接中传输大数据量
- 通过三次握手机制建立连接,可靠协议
- 通信前必须建立连接,效率稍低
- 如:打电话、文件的传输
Java中Socket编程
在Java中网络通信业被称为Socket通信,Socket翻译为:套接字。我们可以理解为“插座”,在通信之前需要安装插座,之后建立管道进行通信。
UDP下Socket通信
- DatagramSocket(UDP插座服务)
- 接收端与发送端都要启动DatagramSocket服务
- 既有接收数据的服务,也有发送数据的服务
- DatagramPacket(数据包类)
TestUdpSender.java
package com.jpzhutech.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class TestUdpSender {
public static void main(String[] args) {
DatagramSocket datagramSocket = null;
DatagramPacket datagramPacket = null;
try {
datagramSocket = new DatagramSocket();
String data = "这是我第一个UDP服务的例子";
try {
datagramPacket = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getLocalHost(), 9090);
try {
datagramSocket.send(datagramPacket);
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
} catch (SocketException e) {
throw new RuntimeException(e);
}finally{
if(datagramSocket != null){
datagramSocket.close();
}
}
}
public void sender(){
}
}
- 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
- 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
TestUdpReceive.java
package com.jpzhutech.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class TestUdpReceive {
public static void main(String[] args) {
DatagramSocket datagramSocket = null;
DatagramPacket datagramPacket = null;
try {
datagramSocket = new DatagramSocket(9090);
byte[] buf = new byte[1024];
datagramPacket = new DatagramPacket(buf, buf.length);
try {
datagramSocket.receive(datagramPacket);
System.out.println("接收到的数据为:"+new String(buf,0,datagramPacket.getLength()));
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (SocketException e) {
throw new RuntimeException(e);
}finally{
if (datagramSocket != null) {
datagramSocket.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
- 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
如果将上述接收端的缓冲数组大小换成5而不是1024,那么很显然不能完全的读取到数据,就会出错,怎么避免这个问题?无解:这是由UDP协议本身决定的。
UDP协议中Socket编程步骤总结
- 发送端
- 建立UDP服务
- 准备数据,把数据封装到数据包中进行发送,发送端的数据包要带上ip地址与端口号。
- 调用UDP的send发送数据
- 关闭UDP服务
- 接收端
- 建立UDP服务
- 准备空的数据包接收数据,只需要知道端口号就行
- 调用UDP的服务接收数据,使用receive()方法
- 关闭UDP服务
UDP协议注意事项
在使用UDP协议进行网络通讯时,必须要注意:
- 对方的IP地址
- 单个人发送:指定其具体的ip地址
- 广播地址:广播地址就是主机号为255的地址,给广播IP地址发送消息的时候,在同一个网络段的机器都能接收到信息,只有在UDP协议中才有广播地址发送这个说法
- 服务的端口号
- 数据包的格式
上述三个条件缺一不可,比如如果你的数据包的格式不对,接收端会拒绝你的数据,接收方会判定为你现在的数据为非法的数据,所以接收不到。每一个网络程序都有自己所处理的特定格式的数据,如果接收到数据不符合指定的格式,就会被当做垃圾数据丢弃(相当于加密),这么做是为了保证安全。
通常定义的数据格式:
version: time: sender: ip: flag(发送的标识符): content(真正要发送的内容)。
出现数据包丢失的情况
什么情况下会出现数据丢失的情况呢?
TCP下Socket通信
TCP通信特点:
- TCP协议是基于IO流进行数据传输的,是面向连接的。
- TCP进行数据传输时数据没有大小限制。
- TCP面向连接,通过三次握手保证数据的完整性,是一个可靠的协议。
- TCP是面向连接的,所以速度慢。
- TCP是严格的区分客户端与服务器端。
-
比如:打电话、文件传输使用TCP协议、下载等。
-
Socket(客户端)
- ServerSocket(服务器端)
TcpClient.java
package com.jpzhutech.tcp;
/**
* 端口被占用的异常可能会经常出现,这都是由于已经有程序在运行
* @author 朱君鹏
*
*/
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class TcpClient {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket(InetAddress.getLocalHost(), 9090);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("服务端你好".getBytes());
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
- 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
- 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
TcpServer.java
package com.jpzhutech.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
public class TcpServer {
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(9090);
InputStream inputStream = serverSocket.accept().getInputStream();
byte[] buf = new byte[1024];
int length = 0 ;
while((length = inputStream.read(buf)) != -1 ){
System.out.println("接收到的数据为:"+new String(buf,0,length));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
- 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
- 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
TCP协议中Socket编程步骤总结
- 建立TCP服务
- 接收客户端的连接产生Socket
- 获取对应的流对象,读取后写出数据
- 关闭资源
TCP协议编程示例
TcpClient.java
package com.jpzhutech.tcp;
/**
* 端口被占用的异常可能会经常出现,这都是由于已经有程序在运行
* @author 朱君鹏
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class TcpClient {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket(InetAddress.getLocalHost(),9090);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("服务端你好".getBytes());
outputStream.flush();
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = 0 ;
while((length = inputStream.read(buf)) != -1 ){
System.out.println("客户端接收到的数据:"+ new String(buf, 0, length) );
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
- 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
- 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
TcpServer.java
package com.jpzhutech.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(9090);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = 0 ;
int count = 0 ;
while((length = inputStream.read(buf)) != -1 ){
System.out.println("服务器端接收到的数据为:"+new String(buf,0,length));
}
OutputStream outputStream = socket.getOutputStream();
String string = "客户端你也好啊!";
outputStream.write(string.getBytes());
outputStream.flush();
String string2 = "你找我干嘛?";
System.out.println(string2);
socket.shutdownOutput();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
- 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
- 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
将上述的代码改造,得到可以产生类似QQ中问答效果的代码。
ChatClient.java
package com.jpzhutech.tcpqq;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
public class ChatClient {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket(InetAddress.getLocalHost(), 9090);
OutputStream outputStream = socket.getOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(inputStream));
while((line = bufferedReader.readLine()) != null){
outputStreamWriter.write(line+"\r\n");
outputStreamWriter.flush();
line = bufferedReader2.readLine();
System.out.println("服务端回送的数据是:" + line);
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally{
if(socket != null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
- 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
- 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
ChatServer.java
package com.jpzhutech.tcpqq;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ChatServer {
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(9090);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
socket.getOutputStream());
BufferedReader bufferedReader1 = new BufferedReader(
new InputStreamReader(System.in));
while((line = bufferedReader.readLine()) != null){
System.out.println("服务器接收到的数据:"+line);
System.out.println("请输入回送给客户端的数据");
line = bufferedReader1.readLine();
outputStreamWriter.write(line+"\r\n");
outputStreamWriter.flush();
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally{
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
- 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
- 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
山寨Tomcat服务器
对上述ChatClient.java和ChatServer.java代码的思考,Tomcat服务器实际上的原来和该代码的原理类似,客户端给服务器发一个请求,服务器端会对相应的请求进行处理,上面的代码实现的是客户端说一句话,服务器端会对你说出的话进行响应,很相似,我们可以针对上述代码进行改造,实现一个山寨的Tomcat服务器。实际上浏览器与服务器之间的通讯就是使用了TCP协议。浏览器就是客户端,有兴趣的朋友可以自己实现。