一.概述
网络编程,意在编写和网络相关的代码,主要实现利用网络进行交互的程序,而不仅限于与本地的程序,数据,用户进行交互与信息传递。无论是什么语言的网络编程,无非是要解决以下两个问题
1. 定位
2. 传输
在网络中,不管你是服务端还是客户端,都只是对这个网络接口所具有的职能的一种称呼,在网络中抽象出来,他们都是点对点之间进行传输的两个点而已。所以我们暂且不谈什么客户端,服务端,只求如何利用这个网络进行信息的交互。
第一点:定位
在网络中如何定位一个点,其实也就是如何定位一个主机。IP地址。在主机中又是如何定位那个与我产生信息交互的程序呢?端口号。所以由IP地址加上端口号即可完成一个传输对象的定位。在Java语言中,则使用的是InetAddress类,来封装IP地址的。即在Java中InetAddress对象就代表了一个IP地址,你如果要操作InetAddress类就是在操作一个主机的IP。
创建一个IP地址,即InetAddress对象
InetAddress ip1 = InetAddress.getByName("www.csdn.com");
InetAddress对象只能通过静态方法获得,一般常用的是上面这个getByName()方法,参数为域名,还有一个方法为:
static InetAddress getByAddress(byte[] addr)
Returns an InetAddress object given the raw IP address .
常用的方法还有:
1. getHostName() 返回域名
2. getHostAddress() 返回IP地址
另一个在主机中定位的则是端口号,标识这正在计算机上运行的进范围时0~65535,其中0~1023被预先定义了,一般使用靠后一些的,而端口号与IP地址的组合就成为了网络套接字(Socket)。所以网络编程又叫做Socket编程。意思就是程序与程序之间的通信要借助于Socket对象来实现定位与传输,接下来我们就进入对于Socket的编程。
Socket
随着C/S架构的普及,现在网络上基本上都是这种客户端与服务端进行通信的模式。Java中Socket与ServerSocket即对应着网络中的客户端与服务端。
如何构造一个Socket对象:
通常都是用Socket的构造方法来获得常用的是:
Socket socket = new Socket(InetAddress.getByName(host),port);
而ServerSocket对象的构造常用:
ServerSocket ss = new ServerSocket(port);
y因为服务端一般只用接受请求即可,所以只需要端口号即可,如果需要返回数据给客户端,就需要使用Socket类来完成。ServerSocket含有一个accept() 方法,用来返回所连接的那个客户端的Socket对象。
所以Socket,ServerSocket中分别包含着客户端与服务端的位置信息,即IP地址与端口号,接下来就是传输问题。
第二点:传输
z在主流的TCP/IP协议中,传输协议其实有两种分别是TCP(传输控制协议),UDP(用户数据报协议)。这两种方法都各有各的优点适用与不同的场景。
TCP协议:是一种稳定可靠的传输协议,需要传输两端进行3次握手,建立稳定的传输通道后才可以进行传输。传输特点是,传输稳定,安全,传输数据量大
UDP协议:是一种不可靠的传输协议,每个数据报内都包含着,数据源,目的信息等封装在包内,包的指定大小为64K。发送端和接收端都不知道何时会送达,是否被接受等。但是这种方式在传输小数据时效率很高,也有着自己的适用场景
TCP传输
在TCP传输中,需要对客户端,服务端程序进行编写,以下为编写思路:
客户端:
- 创建一个Socket对象,通过构造器指明服务端IP地址以及端口号
- 适用流操作getOutStream(),发送数据,方法返回OutputStream的对象
- 具体的输出过程
- 关闭相应的流操作
以下是示例代码:
public void client(){
Socket socket = null;
java.io.OutputStream os = null;
try {
socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
os = socket.getOutputStream();
os.write("我是客户端".getBytes());
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(os != null){
try{
os.close();
}catch(IOException e){
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
服务端:
- 创建一个ServerSocket对象,通过构造器指明自身的端口号
- 调用其accept()方法,返回Socket对象
- 调用Socket对象的getInputStream()方法获取一个从客户端发送过来的输入流
- 对获取的输入流进行操作
- 关闭相应的流,以及Socket,ServerSocket
以下时示例代码:
public void server(){
ServerSocket ss = null;
Socket s = null;
InputStream is = null;
try {
ss = new ServerSocket(9090);
s = ss.accept();
is = s.getInputStream();
byte[] b = new byte[20];
int len;
while((len = is.read(b)) != -1){
String str = new String(b,0,len);
System.out.println(str);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(is != null){
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(s != null){
try {
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}if(ss != null){
try {
ss.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
UDP传输
类DatagramSocket,DatagramPacket 实现了给予UDP的实现。DatagramSocket实现发送于接收操作,DatagramPacket则包含着所有的地址与数据信息。
发送端与接收端的操作流程相似,只不过一个时发送方法,一个是接收方法。
1. 创建一个DatagramSocket对象,一个DatagramPacket对象。
2. 将数据以及IP地址,端口号封装到DatagramPacket对象中
3. 调用Datagrams对象进行发送或接受
以下为示例代码
public void send(){
DatagramSocket ds = null;
try {
ds = new DatagramSocket();
byte[] b = "我是数据".getBytes();
DatagramPacket pack = new DatagramPacket(b,0,b.length,InetAddress.getByName("127.0.0.1"),9090);
ds.send(pack);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(ds != null){
ds.close();
}
}
}
public void receive(){
DatagramSocket ds = null;
try {
ds = new DatagramSocket(9090);
byte[] b = new byte[1024];
DatagramPacket pack = new DatagramPacket(b,0,b.length);
ds.receive(pack);
String str = new String(pack.getData(),0,pack.getLength());
System.out.println(str);
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(ds != null){
ds.close();
}
}
}
注:在TCP传输中,如果客户端与服务端有很多信息来回交互的话,由于InputStream方法中的read方法会一直等待输入,即使你自己输入完毕,它依然会等待而导致的阻塞,使得后续的交互无法完成,这个时候就需要使用Socket对象的shutdownOutput()方法告诉服务端已经输出完毕,服务端的输入才会停止接受,开始执行输入以后的操作。