-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
Java基础之网络编程
1.网络编程基本概念
网络编程的本质是两个设备之间通过网络的数据交换,在计算机网络中,设备主要指计算 机。概括的说是把一个设备中的数据发送给另外的一个设备,然后 接受另外一个设备反馈的数据。现在的网络编程基本上都是基于请求/响应方式的,也就是一个设备发送请求数据给另外一个,然后接收另一个设备的反馈。
在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端(Client),等待其他程序连接的程序被称作服务器(Server)。客户端程序可以在需要的时候启动,而服务器为了能够时刻相应连接,则需要一直启动。
例如以打电话为例,首先拨号的人类似于客户端,接听电话的人必须保持电话畅通类似于服务器。连接一旦建立以后,就客户端和服务器端就可以进行数据传递了,而且两者的身份是等价的。在一些程序中,程序既有客户端功能也有服务器端功能,最常见的软件就是 BT、emule 这类软件了。
网络模型(如下图)
OSI参考模型
TCP/IP参考模型
2、网络通讯要素
IP地址:
InetAddres:网络中设备的标识,不易记忆,可用主机名;
本地回环地址:127.0.0.1 主机名:localhost;
端口号:
用于标识进程的逻辑地址,不同进程的标识
有效端口:0~65535,其中0~1024系统使用或保留端口。
备注:不是所谓的物理端口!
传输协议:通讯的规则
常见协议:TCP,UDP。
3、TCP和UDP的联系和用途
一.区别:
二者都是有用的和常用的,可直接从功能上进行区分,简单明了:
a.这两种传输协议也就是合于适配不同的业务和不同的硬件终端。在使用中,类似于图像、声音等对可靠性要求没有那么高的业务可以用UDP,他们不 需要准确存储对准确性无要求但要求速度快;类似于文本、程序、文件等要求可靠的数据最好就用TCP,但会牺牲一些速度,对系统资源的要求:CP较多,UDP少。
b.程序结构:UDP程序结构较简单,TCP复杂。流模式与数据报模式:TCP保证数据正确性,UDP可能丢包;TCP保证数据顺序,UDP不保证。
二.用途
TCP是面向连接的,有比较高的可靠性,一些要求比较高的服务一般使用这个协议,如FTP、Telnet、SMTP、HTTP、POP3等,而UDP是面向无连接的,使用这个协议的常见服务有DNS、SNMP、QQ等。对于QQ必须另外说明一下,QQ2003 以前是只使用UDP协议的,其服务器使用8000端口,侦听是否有信息传来,客户端使用4000端口,向外发送信息(这也就不难理解在一般的显IP的QQ版本中显示好友的IP地址信息中端口 常为4000或其后续端口的原因了),即QQ程序既接受服务又提供服务,在以后的QQ版本中也支持使用TCP协议了。
Udp是一种面向无连接的通信协议,该协议使得数据传输的速度得到大幅度的提高。视频聊天语音聊天基本都是用UPD协议。
2.网络编主要的类
网络编程用来的API主要在java.net包中,根据通讯规则不同,可以进行分类如下:
2.UPD通讯规则下的编程
基本步骤:
服务端:
* 建立一个UDP的socket服务,要明一个端口号(此服务会监听这个端品口);
* 准备一个数据报包对象DatagramPacket,用于封装接收到的数据;
* 通过socket服务对象的receive(DatagramPacket)方法,接收数据;
* 处理接收回来的数据(解析DatagramPacket);
* 关闭资源;
客户端:
* 建立一个UDP的socket服务(可不明确端口号,系统自动配置);
* 准备数据,并将要发送的数据封装到数据包中(DatagramPacket);
* 通过socket服务的send(DatagramPacket)方法将数据包发送出去;
* 关闭资源;
演示代码(简的网络聊天程序):
***********************************************************************
//用户一:
public class UdpRece {
public static void main(String[] args) throws IOException {
new Thread(new UdpRece1()).start();//接收端
new Thread(new UdpSend1()).start();//发送端
}
}
// 接收信息程序(9080端口)
class UdpRece1 implements Runnable {
private DatagramSocket ds;
UdpRece1() {
try {
this.ds = new DatagramSocket(9080);
} catch (SocketException e) {
}
}
@Override
public void run() {
System.out.println("用户1接收端开启");
String data;// 接收对方接发过来的信息
do {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(
buf, buf.length);
try {
ds.receive(dp);
} catch (IOException e) {
}
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
data = new String(
dp.getData(), 0, dp.getData().length);
if (data.equals("close")) {
break;
}
System.out.println("---------
本次接收到的数据为:-----------");
System.out.println(ip);
System.out.println(port);
System.out.println(data);
System.out.println("---------------
------------------");
} while (true);
ds.close();
return;
}
}
//发送信息程序
class UdpSend1 implements Runnable {
private DatagramSocket da;
UdpSend1() {
try {
this.da = new DatagramSocket();
} catch (SocketException e) {
}
}
@Override
public void run() {
System.out.println("用户1发送端开启");
do {
System.out.println("请输入你要发送的内
容(输入'close'退出!):");
Scanner scanner = new Scanner(System.in);
String ss = scanner.next();
if (ss.equals("close")) {
break;
}
// 准备数据,并将数据、要发送的地地址,对方端口号
三个信息封装到数据报包对象中
byte[] data = ss.getBytes();
DatagramPacket dp = null;
try {
dp = new DatagramPacket(data, data.length,
InetAddress.getByName("192.168.1.100"), 9081);
} catch (UnknownHostException e) {
e.printStackTrace();
}
// 将数据报包发出去
try {
da.send(dp);
} catch (IOException e) {
}
} while (true);
da.close();
return;
}
}
***********************************************************************
//用户二
public class UdpSend {
public static void main(String[] args) throws IOException {
new Thread(new UdpRece2()).start();//接收端
new Thread(new UdpSend2()).start();//发送端
}
}
//接收信息程序(9080端口)
class UdpRece2 implements Runnable {
private DatagramSocket ds;
UdpRece2() {
try {
this.ds = new DatagramSocket(9081);
} catch (SocketException e) {
}
}
@Override
public void run() {
System.out.println("用户2接收端开启");
String data;// 接收对方接发过来的信息
do {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(
buf, buf.length);
try {
ds.receive(dp);
} catch (IOException e) {
}
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
data = new String(dp.getData(),
0, dp.getData().length);
if (data.equals("close")) {
break;
}
System.out.println("---------本次接收到的数据为:---
--------");
System.out.println(ip);
System.out.println(port);
System.out.println(data);
System.out.println("----------------------------
-----");
} while (true);
ds.close();
return;
}
}
//发送信息程序:
class UdpSend2 implements Runnable {
private DatagramSocket da;
UdpSend2() {
try {
this.da = new DatagramSocket();
} catch (SocketException e) {
}
}
@Override
public void run() {
System.out.println("用户2发送端开启");
do {
System.out.println("请输入你要发送的内容(输
入'close'退出!):");
Scanner scanner = new Scanner(System.in);
String ss = scanner.next();
if (ss.equals("close")) {
break;
}
// 准备数据,并将数据、要发送的地地址,对方端口号三
个信息封装到数据报包对象中
byte[] data = ss.getBytes();
DatagramPacket dp = null;
try {
dp = new DatagramPacket(data, data.length,
InetAddress.getByName("192.168.1.100"), 9080);
} catch (UnknownHostException e) {
e.printStackTrace();
}
// 将数据报包发出去
try {
da.send(dp);
} catch (IOException e) {
}
} while (true);
da.close();
return;
}
}
***********************************************************************
2.TCP通讯规则下的编程
基本步骤:
服务端:
* 1.创建TCP的ServerSocket服务(socket),需要指定监听的端口号;
* 2.通过ServerSocket的accept()方法得到与客户端的socket对象;
* 3. 通过客户端的socket对象得到连接的输入流InputStream;
* 4.从输入流程中读取客户端发送过来的数据,通过socket对象解析对方发送相关的信息。
* 5.关闭资源;
客户端:
* 1.创建TCP的socket服务(socket),需要对方指定IP地址及端口号;
* 2.拿到socket输出流OutputStream;
* 3.通过这个流程将数据写出去;
* 4.关闭资源;
演示代码:
***********************************************************************
//服务端(接收端)
public class Tcp_Rece {
public static void main(String[] args) throws IOException {
//创建ServerSocket服务
ServerSocket serverSocket = new ServerSocket(10086);
//监听端口,得到客户端的Socket对象
Socket socket = serverSocket.accept();
//从客户端的Socket对象中得到输入流
InputStream in = socket.getInputStream();
//从输入流是读取数据
int len;
byte[] bfu = new byte[1024];
while ((len = in.read(bfu)) != -1) {
System.out.println(new String(bfu, 0, len));
}
System.out.println("接收成功");
//关闭资源
socket.close();
serverSocket.close();
}
}
//发送端(客户端)
public class Tcp_Send {
public static void main(String[] args) throws UnknownHostException,
IOException {
// 建议服务
Socket socket = new Socket("192.168.1.100", 10086);
// 得到输出流OutputStream
OutputStream outs = socket.getOutputStream();
// 输出数据
outs.write("Tcp测试数据".getBytes());
System.out.println("发送成功");
//关闭资源
socket.close();
}
}
***********************************************************************
以上代码可以利用多线程的技术进行重构,实现简单的即时聊天程序,由于实现思路与以述UDP的聊天程序基本一致,代码略去。
小案例:利用TCP协议编写一个写小转大写的客户端/服务端的网络程序,具体代码如下:
***********************************************************************
/*
* 服务端
*
* */
public class Service {
public static void main(String[] args) throws IOException {
System.out.println("服务端开启");
// 创建一个ServerSocket的服务端,监听一个端口
ServerSocket ss = new ServerSocket(10086);
// 得到客户端的Socket
Socket sk = ss.accept();
// 从客户端的Socket中得到输入流InputSource
InputStream in = sk.getInputStream();
// 从客户端的Socket中得到输出流OutputStream
OutputStream out = sk.getOutputStream();
while (true) {
// 从InputSource中得到客户端发过来的数据
int len;
byte[] bfu = new byte[1024];
while ((len = in.read(bfu)) != -1) {
// 如果客输入的是close,则退出
if (bfu.toString().equals("close")) {
ss.close();
break;
}
// 将数据转化为大写的
byte[] data = new String(bfu, 0, len).toUpperCase().getBytes();
// 通过OutputStream将数据输出给客户端
out.write(new String("转换
结果为:").getBytes());
out.write(data, 0, data.length);
System.out.println("转换结果为:" +
new String(data, 0, data.length));
}
}
// 关闭资源(实际情况下服务端是一直处于开启状态的)
// sk.close();
}
}
/*
* 客户端
*
* */
public class Consumer {
public static void main(String[] args) throws IOException {
System.out.println("客户端开启");
// 创建Socket的服务端
Socket sk = new Socket("192.168.1.100", 10086);
// 从服务中得到输出流OutputStream
OutputStream out = sk.getOutputStream();
// 从服务中得到输入流InputSource
InputStream in = sk.getInputStream();
// 从控制台输入数据
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("输入你要转换的字母:");
String data = scanner.next();
// 如果输入的是close就退出
if (data.equals("close")) {
break;
}
// 用输出流将数据写到服务端
out.write(data.getBytes());
// 从输入流中得到服务端的响应信息
byte[] bfu = new byte[1024];
in.read(bfu);
// 将信息显示在控制台上
System.out.println(new String(bfu, 0, bfu.length));
System.out.println("-----接收成功------");
}
sk.close();
}
}
***********************************************************************