Java网络编程提供了两种协议:TCP(传输控制协议)和UDP(数据报协议)。TCP(Transmission Control Protocol)是一种可靠的传输协议,传输时会采用“三次握手”端的方式建立连接,以保证数据的可靠性和安全性;而UDP(User Datagram Protocol)协议是一种不可靠的传输协议,发送的数据不一定能够接受的到,网上的聊天是工具一般采用的此协议。下面将详细的接受TCP和UDP的使用以及相应的编码。
一、TCP网络通信
Java中使用Socket(套接字)实现TCP。服务器端使用ServerSocket类,客户端使用Socket类实现。
1、ServerSocket类
其主要有如下的方法:
ServerSocket() 创建非绑定服务器套接字。
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
Socket accept() 侦听并接受到此套接字的连接。
void close() 关闭此套接字
boolean isClosed() 返回 ServerSocket 的关闭状态。
InetAddress getInetAddress() 返回此服务器套接字的本地地址。
void bind(SocketAddress endpoint) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。
boolean isBound() 返回 ServerSocket 的绑定状态。
SocketAddress getLocalSocketAddress() 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。
服务器端每次运行是都要使用accept()方法等待客户端的连接,此方法执行之后服务器端将进入阻塞状态,知道客户端连接之后程序才继续执行,此方法返回是Socket,代表一个客户端对象。
2、Socket类
两台机器连接通信的端点。主要使用如下方法:
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
OutputStream getOutputStream() 返回此套接字的输出流。
InputStream getInputStream() 返回此套接字的输入流。
boolean isBound() 返回套接字的绑定状态。
boolean isClosed() 返回套接字的关闭状态。
boolean isConnected() 返回套接字的连接状态。
void close() 关闭此套接字。
其通过字节流和服务端进行通信。
实例一、下面是实现一个简单的TCP客户端可服务器端通信。
(1)服务器端:
package andy.network.test;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author Zhang,Tianyou
* version:2014-11-25 上午10:49:11
*
*
*/
public class FirstTCPServer {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//创建服务器
ServerSocket server = null;
Socket socket = null;
PrintStream out = null;
//服务器在9999上等待客户端的访问
server = new ServerSocket(9999);
System.out.println("服务器端已经运行,等待客户的响应。。。");
//程序阻塞 等待客户端的访问
socket = server.accept();
//向客户端输出信息
String str = "hello andy.";
out = new PrintStream(socket.getOutputStream());
out.print(str);
out.close();
socket.close();
server.close();
}
}
(2)客户端:
package andy.network.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author Zhang,Tianyou
* version:2014-11-25 上午11:02:17
*
*
*/
public class FirstTCPSocket {
/**
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException,
IOException {
// 定义客户端
Socket client = null;
// 设置地址和端口
client = new Socket("localhost", 9999);
BufferedReader buf = null; // 结束服务端流
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
String str = buf.readLine();
System.out.println("服务器端通知信息:" + str);
buf.close();
client.close();
}
}
实例二
实现客户端和服务器端的通信,服务器端可以响应多个客户端,每个客户端请求就启动一个线程。
(1)服务端
package andy.network.test;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author Zhang,Tianyou
* version:2014-11-25 上午11:41:22
*
* 聊天服务器端
*/
public class ChatServer {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
ServerSocket server = null;
Socket client = null;
// 监听9999端口的连接
server = new ServerSocket(9999);
boolean flag = true;
while (flag) {
System.out.println("服务器运行,等待客户端连接。");
client = server.accept();
// 对每一个客户端请求启动一个线程
new Thread(new ChatThread(client)).start();
}
server.close();
}
}
对应服务端线程任务:
package andy.network.test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
/**
* @author Zhang,Tianyou
* version:2014-11-25 上午11:24:26
*
* 使用多线程机制,在每个用户响应之后启动一个新任务
*
*/
public class ChatThread implements Runnable {
// 接受客户端
private Socket client = null;
public ChatThread(Socket client) {
this.client = client;
}
@Override
public void run() {
// 向客户端发送数据
PrintStream out = null;
// 接收客户端发来的数据
BufferedReader buff = null;
try {
// 得到客户端的输入信息
buff = new BufferedReader(new InputStreamReader(
client.getInputStream()));
out = new PrintStream(client.getOutputStream());
//标志是否一个客户端的操作完毕
boolean flag = true;
while (flag) {
//不断的接收信息
String str = buff.readLine();
if(str == null || "".equals(str)){
flag = false;//客户端操作结束
}else{
if("bye".equals(str)){
flag = false;
out.println("bye-bye");
}else {
//向客户端回显输入
out.println("you input:" + str);
}
}
}
out.close();
client.close();
} catch (Exception e) {
System.out.println("服务器异常!");
}
}
}
(2)客户端
package andy.network.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author Zhang,Tianyou
* version:2014-11-25 上午11:53:03
*
* 聊天客户端程序
*
*/
public class ChatClient {
public static void main(String[] args) throws UnknownHostException,
IOException {
Socket client = null;
client = new Socket("localhost", 9999);
// 接收服务器端的信息
BufferedReader buff = null;
// 向服务器端发送数据
PrintStream out = null;
BufferedReader input = null;
// 获取键盘输入数据
input = new BufferedReader(new InputStreamReader(System.in));
out = new PrintStream(client.getOutputStream());
buff = new BufferedReader(
new InputStreamReader(client.getInputStream()));
// 标志位
boolean flag = true;
while (flag) {
System.out.println("输入信息:");
String str = input.readLine();
// 向服务器端输出信息
out.println(str);
if ("bye".equals(str)) {
flag = false;
} else {
String chatCont = buff.readLine();
System.out.println(chatCont);
}
}
client.close();
buff.close();
}
}
二、UDP网络通信
TCP的通信是建立在可靠通信的基础上,UDP则是不可靠的通信,使用数据报形式发送数据。
在Java中主要有DatagramPacket和DatagramSocket实现。
1、DatagramPacket主要方法
DatagramPacket(byte[] buf, int length)
构造 DatagramPacket,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
byte[] getData()
返回数据缓冲区。
int getLength()
返回将要发送或接收到的数据的长度。
2、DatagramSocket主要方法
DatagramSocket(int port)
创建数据报套接字并将其绑定到本地主机上的指定端口。
void receive(DatagramPacket p)
从此套接字接收数据报包。
void send(DatagramPacket p)
从此套接字发送数据报包。
在使用UDP时,先使用客户端指定好要接受的数据和端口。然后开启服务端发送数据。
实现如下:
(1)客户端
package andy.network.test;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author Zhang,Tianyou
* version:2014-11-25 下午3:04:10
*
* UDP客户端
*/
public class UDPClient {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 客户在9999端口监听
DatagramSocket dSocket = null;
dSocket = new DatagramSocket(9999);
// 定义接收数据的字节长度
byte[] buff = new byte[1024];
DatagramPacket dPacket = null;
// 指定接收数据的长度1024
dPacket = new DatagramPacket(buff, 1024);
System.out.println("等待接受数据。。");
//接收数据
dSocket.receive(dPacket);
String str = new String(dPacket.getData(), 0, dPacket.getLength())
+ "from" + dPacket.getAddress().getHostAddress() + ":"
+ dPacket.getPort();
System.out.println(str);
//关闭数据报套接字
dSocket.close();
}
}
(2)服务器端
package andy.network.test;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author Zhang,Tianyou
* version:2014-11-25 下午3:13:38
*
* UDP服务器端
*
*/
public class UDPServer {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
DatagramSocket dSocket = null;
//服务端在3333端口监听
dSocket = new DatagramSocket(3333);
DatagramPacket dPacket = null;
//要发生的数据
String str = "hello andy!";
//向目标端口地址发送数据报
dPacket = new DatagramPacket(str.getBytes(), str.length(),
InetAddress.getByName("localhost") , 9999);
System.out.println("发送数据报");
dSocket.send(dPacket);
dSocket.close();
}
}