标签:
网络编程主要是实现计算机与计算机之间的数据传输问题,网络编程要与网页编程概念区分开来,Java一般不用来做桌面应用软件,PC机基本不用网络编程,但是如果以后要学习的是安卓方向,那么就会用到网络编程知识。那么为什么又要学习网络编程呢?因为在Java SE的小项目中,设计到网络编程、GUI编程的等都集合在一起的项目才是练手的最好项目,所以我觉得还是要好好学习网络编程。在网络编程中应该学习的内容是:分布在不同地域的计算机通过外部设备实现消息、数据、资源共享。
IP地址的本质就是一个由32位的二进制数组成的数据,但是为了便于人们读取,将其中的8位作为一个单元,切分成了4份,就形成了现在的方式,比如:192.168.101.44等,每一个切分后的块可表示的范围是0-255。
IP地址等于网络号+主机号构成,其中子网掩码中未255的部分全部都为网络号,IP地址可以分为三类:
在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(); //得到本机的IP地址,如果没装网卡就没有IP地址
String hostName = inetAddress.getHostName();
//System.out.println(hostName); //显示本机的主机名
String hostAddress = inetAddress.getHostAddress();
System.out.println(hostAddress); //显示本机的ip地址
/*获取别人的ip地址
InetAddress byName = InetAddress.getByName("DESKTOP-G56LBO2"); //根据ip或者主机名就能得到相应的主机信息
String hostName = byName.getHostName();
System.out.println(hostName);
*/
/* 根据域名得到ip地址的数组,这种情况也常会发生
InetAddress[] allByName = InetAddress.getAllByName("www.baidu.com"); //为什么该方法会返回一个数组呢?String host可以写域名也可以写ip还可以写主机名,baidu的一个域名就注册了两个ip地址
for(int i = 0 ; i < allByName.length;i++){
InetAddress inetAddress2 = allByName[i];
String hostAddress = inetAddress2.getHostAddress();
System.out.println(hostAddress);
String hostName = inetAddress2.getHostName();
System.out.println(hostName);
}
*/
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
}
端口在Java中并没有使用类来描述,因为端口号仅仅是一个标识符,端口号从0-65535的范围,其它的无效,但是我们都能用0-65535吗?回答是不能,0-1023系统绑定了一些服务,不能用,如果用会报错。1024-49151系统绑定了一些松散的服务,松散的含义是有一些端口可能没被占用,有些被占用了,也可以使用这里的端口,如果发生冲突换一个端口就好了。1024-65535我们可以使用,如果发生冲突就换一个其他的端口号。49152-65535动态或者私有的端口,我们可以随便用,在使用之前使用cports查看你想使用的端口是否有端口占用。
在Java中网络通信业被称为Socket通信,Socket翻译为:套接字。我们可以理解为“插座”,在通信之前需要安装插座,之后建立管道进行通信。
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) {
//建立UDP服务,很像码头运送货物的场景,在建立UDP时不需要指定端口等信息,这些信息应该被封装在数据包中
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);//第三个参数指的是发送的IP地址,最后一个参数是端口号
//调用UDP的服务发送数据包
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(){
}
}
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 {
//建立UDP服务,接收端必须套时时刻刻监听着发送端的端口,因为发送端根本不管接收端是否准备好,直接发送数据
datagramSocket = new DatagramSocket(9090);
//建立一个数据包的对象,发送端发来的数据会被放在该buf数组中
byte[] buf = new byte[1024];
datagramPacket = new DatagramPacket(buf, buf.length);
//调用UDP服务接收数据
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();
}
}
}
}
如果将上述接收端的缓冲数组大小换成5而不是1024,那么很显然不能完全的读取到数据,就会出错,怎么避免这个问题?无解:这是由UDP协议本身决定的。
在使用UDP协议进行网络通讯时,必须要注意:
上述三个条件缺一不可,比如如果你的数据包的格式不对,接收端会拒绝你的数据,接收方会判定为你现在的数据为非法的数据,所以接收不到。每一个网络程序都有自己所处理的特定格式的数据,如果接收到数据不符合指定的格式,就会被当做垃圾数据丢弃(相当于加密),这么做是为了保证安全。
通常定义的数据格式:
version: time: sender: ip: flag(发送的标识符): content(真正要发送的内容)。
什么情况下会出现数据丢失的情况呢?
TCP通信特点:
比如:打电话、文件传输使用TCP协议、下载等。
Socket(客户端)
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 {
// 建立TCP服务
socket = new Socket(InetAddress.getLocalHost(), 9090);
// 获取socket对象的输出流对象
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);
}
}
}
}
}
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 {
//建立服务器端TCP连接
serverSocket = new ServerSocket(9090);
//建立输入流对象,其中accept方法也是阻塞型的方法,用于接收客户端的连接,如果没有客户端与其进行连接时,会一直等
InputStream inputStream = serverSocket.accept().getInputStream();//想要使用InputStream,但是只有Socket才有,只能使用accept方法获取
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);
}
}
}
}
}
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 {
// 建立TCP服务
socket = new Socket(InetAddress.getLocalHost(),9090);
// 获取socket对象的输出流对象
OutputStream outputStream = socket.getOutputStream();
// 利用输出流对象把数据写出即可
outputStream.write("服务端你好".getBytes());
outputStream.flush();
socket.shutdownOutput(); //关闭输出流,如果想知道为什么,注释该语句会看到其中的原因,参见文章:http://wiki.jikexueyuan.com/project/java-socket/socket-read-deadlock.html
//获取输入流对象,读取服务端回送的数据
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);
}
}
}
}
}
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 {
//建立服务器端TCP连接
serverSocket = new ServerSocket(9090);
//建立输入流对象,其中accept方法也是阻塞型的方法,用于接收客户端的连接,如果没有客户端与其进行连接时,会一直等
Socket socket = serverSocket.accept(); //拿到与服务器端建立连接的客户端
InputStream inputStream = socket.getInputStream();//想要使用InputStream,但是只有Socket才有,只能使用accept方法获取
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(); //关闭输出流,如果想知道为什么,注释该语句会看到其中的原因,只有有了该语句,在客户端的read(buf)方法中才会在最后返回-1,程序才能结束,否则会造成死锁
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
将上述的代码改造,得到可以产生类似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);
}
}
}
}
}
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 socket = serverSocket.accept();
//获取socket的输入流对象
InputStream inputStream = socket.getInputStream(); //得到字节流
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
// 获取socket的输出流
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);
}
}
}
}
}
对上述ChatClient.java和ChatServer.java代码的思考,Tomcat服务器实际上的原来和该代码的原理类似,客户端给服务器发一个请求,服务器端会对相应的请求进行处理,上面的代码实现的是客户端说一句话,服务器端会对你说出的话进行响应,很相似,我们可以针对上述代码进行改造,实现一个山寨的Tomcat服务器。实际上浏览器与服务器之间的通讯就是使用了TCP协议。浏览器就是客户端,有兴趣的朋友可以自己实现。
标签:
原文地址:http://blog.csdn.net/jpzhu16/article/details/51762025