网络编程
1、网络编程基本概念
- 1、什么是计算机网络
- 把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成一个规模大、功能强的网络系统,从而使众多的计算机可以方便的互相传递信息,共享硬件、软件、数据信息等资源、
- 2、计算机网络的主要功能
- 资源共享
- 信息传输与集中处理
- 均衡负荷与分布处理
- 综合信息服务(www/综合业务数字网络ISDN)等
- 3、网络通信协议
- 要使计算机连成的网络互相通信,需要对数据传输速率、传输代码、代码结构、传输控制步奏、出错控制等制定一组标准,这一组共同遵守的通信标准就是网络通信协议,不同的计算机之间必须使用相同的通讯协议才能进行通信
- 4、网络通信接口
- 为了使两个节点之间进行对话,必须在他们之间建立通信工具(接口),使彼此之间能够进行信息交换,接口包括两部分
- 1、硬件装置:实现节点之间的信息传递
- 2、软件装置:规定双方进行通信的约定协议
- 为了使两个节点之间进行对话,必须在他们之间建立通信工具(接口),使彼此之间能够进行信息交换,接口包括两部分
- 5、程序开发结构
- 网络编程主要是指完成C/S程序的开发,程序的开发结构有以下两种
- C/S(客户端/服务器端)
- 开发两套程序,两套程序都需要维护,例如 QQ,CS程序一般比较稳定
- B/S(游览器端/服务端)
- 开发一套程序,客户端使用游览器进行访问,例如:各个论坛,BS程序一般稳定性差,而且安全性较差,但是C/S的程序开发在实际的java应用中毕竟很少了,而且整个java基本上都是B/S为主
- C/S程序主要可以完成以下两种程序的开发:
- TCP:传输控制协议,采用三方握手的方式,确保准确的连接操作
- UDP:数据报协议,发送数据报,例如:手机短信或者QQ消息
- TCP/UDP的数据帧格式简答图例:
- 协议类型 | 源IP | 目标IP | 源端口 | 目标端口 | 帧序号 | 帧数据
- 其中协议类型用于区分TCP/UDP
2、网络编程TCP协议
- 传输控制协议/因特网互联协议,又称为网络通讯协议,这个协议是Internet最基本的协议,Internet国际互联网络的基础,简单的说就是由网络层的IP协议和传输层TCP协议组成的
- IP地址:网络中每台计算机的一个标识号码
- 本机IP:127.0.0.1 localhost
- 端口号(PORT):端口号的范围065535之间,01023之前的端口数是用于一些知名的网络服务和应用
- 应用层-->表示层-->会话层-->传输层-->网络层-->数据链路层--->物理层
- 1、TCP程序概述
- TCP是一个可靠的协议,面向连接的协议
- 实现TCP程序,需要编写服务器端和客户端,Java API为我们提供了java.net包,为实现网络应用程序提供类
- ServerSocket:此类实现服务器套接字
- Socket:此类实现客户端套接字(也可以叫“套接字”)
- Socket是网络驱动层提供给应用程序编程的接口和一种机制
- TCP是一个可靠的协议,面向连接的协议
- 2、实现服务器端和客户端程序
-
服务器端
public class ServerSocket extends Object
此类实现服务器套接字,服务器套接字等待请求通过网络传入,它基于该请求执行某些操作,然后可能向请求者返回结果。
-
ServerSocket(int port)
创建绑定到特定端口的服务器套接字
void setSoTimeout(int timeout)
通过制定超市值启用/禁用SO_TIMEIOUT 以毫秒为单位
InetAddress getInetAddress()
返回此服务器套接字的本地地址
Socket accept()
帧听并接受此套接字的连接
* 实现服务器端与客户端程序
* 客户端
public class Socket extends Object
此类实现客户端套接字(也可以就“套接字”),套接字是两台机器间通信的端点
Socket(String host,int port)
创建一个流套接字并将其连接到指定主机上的指定端口号
InputStream getInputStream()
返回次套接字的输入流
OutputStream getOutputStream()
返回此套接字的输出流
void setSoTimeout(int timeout)
启用/禁用带有指定超时值得SO_TIMEOUT,以毫秒为单位
3、TCP实现ECHO程序
服务端步奏:
//服务器端
public static void main(String[] args) {
// 1024--65535
try {
//1、创建ServerSocket对象,绑定监听端口
ServerSocket ss = new ServerSocket(8888);
System.out.println("服务器已经启动,正在等待连接。。。");
//2、通过accept()方法监听客户端的请求
Socket socket = ss.accept(); // 表示客户端等待连接 会阻塞
//3、连接建立后,通过输入流读取客户端发送的请求信息
InputStream in = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String info = br.readLine();
System.out.println(info);
//4、通过输出流向客户端发送响应的信息
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println("demo" + info);
//5、关闭相应的资源
out.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//客户端程序
// 连接服务器
try {
//创建Socket对象,指明需要连接的服务器的地址和端口号
Socket socket = new Socket("localhost", 8888);
System.out.println("连接成功");
// 输出流 连接建立后,通过输出流向服务器发送请求信息
OutputStream out = socket.getOutputStream();
// 输入流 通过输入流获取服务器响应的信息
InputStream in = socket.getInputStream();
PrintStream ps = new PrintStream(out);
ps.println("hello");
BufferedReader br = new BufferedReader(new InputStreamReader(in));
System.out.println(br.readLine());
//关闭相应的资源
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
4、服务器与多客户端通信
目前为止我们编写的程序中,服务器只能处理一个客户端连接,要想服务器同时支持多个客户端的连接,就必须加入多线程的处理机制,将每一个连接的客户端创建一个新的线程对象。
-
应用多线程来实现服务器与多客户端之间的通信
- 基本步奏
- 1、服务器端创建ServerSocket,循环调用accept()等待客户端连接
- 2、客户端创建一个socket并请求和服务器端连接
- 3、服务器端接受客户端请求,创建socket与改客户端建立专线连接
- 4、建立连接的两个socket在一个单独的线程中对话
- 5、服务器端等待新的连接
- 基本步奏
// 服务器端代码
package com.yinlei.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.omg.CORBA.TRANSACTION_REQUIRED;
public class MutilServer {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
try {
ServerSocket ss = new ServerSocket(8000);
System.out.println("服务器已经启动,正在等待连接。。。");
while (true) {
Socket socket = ss.accept(); // 等待客户端连接,阻塞
es.execute(new ClientThread(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
static class ClientThread implements Runnable {
private Socket socket;
public ClientThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
System.out.println("客户端的IP为:" + socket.getInetAddress().getHostName());
InputStream in = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
while (true) {
String info = br.readLine();
if (info==null || "bye".equals("bye")) {
break;
}
System.out.println(info);
ps.println("demo : " + info);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//客户端代码
package com.yinlei.socket;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class MutilSocket {
public static void main(String[] args) {
// 连接服务器
try {
Socket socket = new Socket("localhost", 8000);
System.out.println("连接成功");
boolean flag = true;
// 输出流
OutputStream out = socket.getOutputStream();
// 输入流
InputStream in = socket.getInputStream();
PrintStream ps = new PrintStream(out);
@SuppressWarnings("resource")
Scanner input = new Scanner(System.in);
System.out.println("请输入...");
while (flag) {
String info = input.next();
if ("bye".equals(info)) {
flag = false;
break;
}
ps.println(info);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
info = br.readLine();
System.out.println(info);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//服务器端接收数据
//客户端接收数据,有bye的时候退出
5、多客户端之间的通信
- 客户端的数据包通过服务器中转,发送给另一个客户端
6、网络编程UDP协议
- 1、UDP是User Datagram Protocol简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的原地址,或者目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的,每个被传输的数据报必须限定在64kb之内
- 主要使用以下的两个类
- DatagramPacket:此类表示数据报名字
- DatagramSocket:此类表示用来发送和接收数据报的套接字
//服务器端。。。
String info = "hello"; //将信息封装成数据报
byte[]bytes = info.getBytes();
try {
//客户端在5000端口监听
DatagramPacket dp = new DatagramPacket(bytes, 0,bytes.length,InetAddress.getByName("localhost"),5000);
DatagramSocket server = new DatagramSocket(3000);//服务器的端口
server.send(dp);
server.close();
} catch (Exception e) {
e.printStackTrace();
}
//客户端代码。。。
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);// 接受数据
DatagramSocket client;
try {
client = new DatagramSocket(5000);
client.receive(dp);
System.out.println(new String(dp.getData(), 0, dp.getLength()));
client.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//
7、URL
- 1、URL概述
- URL(uniform resource location)类URL代表一个统一资源定位符,它是值向互联网"资源"的指针,抽象类URLConnection是所有类的超类,它代表应用程序和URL之简单的通信链接
URL url = new URL("");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("c:\\dasd.jpg"));
byte[]bs = new byte[1024];
int len = -1;
while((len = bis.read(bs)) != -1){
bos.write(bs,0,len);
}
bos.close();
bis.close();
System.out.println("success");
下载一个网络图片并且显示;
new Thread() {
//异步下载
@Override
public void run() {
try {
URL url = new URL(image);
try {
InputStream is = url.openStream();
final Bitmap bitmap = BitmapFactory.decodeStream(is);
runOnUiThread(new Runnable() {
//主线程中更新UI
@Override
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
} catch (IOException e1) {
e1.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
8、HttpURLConnection接口
Http通信中,的POST和GET请求方式不同,GET把参数放在URL字符串的后面,传递给服务器,而POST方法的参数是放在http请求中,因此,在编程之前,应当首先明确使用的请求方法,然后在依据使用的方法选择相应的编程方式
HttpURLConnection是继承于URLConnection类,二者都是抽象类,其对象主要通过URL的openConnection方法获得
try {
HttpURLConnection conn = (HttpURLConnection) new URL(image).openConnection();
conn.setDoOutput(true); //post情况下需要设置为true,默认是false
conn.setDoInput(true); //设置是否从httpUrlConnection读入,默认是true
conn.setRequestMethod("POST");
conn.setUseCaches(true); //设置是否用缓存,post请求不用缓存
//设置content-type
conn.setConnectTimeout(3000); //设置连接超时
conn.setReadTimeout(3000); //设置读取超时
//获取输出流,便于想服务器发送新消息
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
//往流里面写请求参数
dos.writeBytes("name=" + URLEncoder.encode("hello","gb2312"));
dos.flush();
dos.close();
//获取输入流,取数据
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
br.readLine(); //用 !=null 来判断是否结束
br.close();
//关闭connection
conn.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
9、UDP
UDP协议(用户数据报协议)是无连接、不可靠、无序的
UDP协议是以数据报作为数据传输的载体
进行数据传输的时候,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后在将数据报发送出去。
-
相关操作类:
- DatagramPacket:表示数据报包
- DatagramSocket:进行端到端通信的类
-
服务器端实现步奏:
- 1、创建DatagragmSocket,指定端口号
- 2、创建DatagramPacket
- 3、接收客户端发送的数据信息
- 4、读取数据
// 1、创建DatagragmSocket,指定端口号
DatagramSocket socket = null;
try {
socket = new DatagramSocket(8888);
} catch (SocketException e) {
e.printStackTrace();
}
// 创建字节数组,用于接收客户端发送的数据
byte[] data = new byte[1024];
// 2、创建DatagramPacket
DatagramPacket packet = new DatagramPacket(data, data.length);
// 3、接收客户端发送的数据信息
try {
socket.receive(packet); //此方法在接收到数据报之前会一直阻塞
} catch (IOException e) {
e.printStackTrace();
}
// 4、读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("我是服务器---》"+ "客户端说:" +info);
//-----------------------------------------------------------------------------
/**
* 向客户端响应数据
*/
//定义客户端的地址、端口号、数据
InetAddress address = null;
address = packet.getAddress();
int port = packet.getPort();
byte[]data1= "欢迎您".getBytes();
//创建数据报,包含响应的数据信息
DatagramPacket packet2 = new DatagramPacket(data1, data1.length,address,port);
DatagramSocket socket2 = null;
try {
socket2 = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
}
try {
socket2.send(packet2);
} catch (IOException e) {
e.printStackTrace();
}
//关闭资源
socket.close();
socket2.close();
- 客户端实现步奏:
- 1、 定义发送的信息
- 2、创建DataframPacket,包含将要发送的信息
- 3、创建DatagramSocket
- 4、发送数据
// 1、创建DatagragmSocket,指定端口号
DatagramSocket socket = null;
try {
socket = new DatagramSocket(8888);
} catch (SocketException e) {
e.printStackTrace();
}
// 创建字节数组,用于接收客户端发送的数据
byte[] data = new byte[1024];
// 2、创建DatagramPacket
DatagramPacket packet = new DatagramPacket(data, data.length);
// 3、接收客户端发送的数据信息
try {
socket.receive(packet); //此方法在接收到数据报之前会一直阻塞
} catch (IOException e) {
e.printStackTrace();
}
// 4、读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("我是服务器---》"+ "客户端说:" +info);
//-----------------------------------------------------------------------------
/**
* 向客户端响应数据
*/
//定义客户端的地址、端口号、数据
InetAddress address = null;
address = packet.getAddress();
int port = packet.getPort();
byte[]data1= "欢迎您".getBytes();
//创建数据报,包含响应的数据信息
DatagramPacket packet2 = new DatagramPacket(data1, data1.length,address,port);
DatagramSocket socket2 = null;
try {
socket2 = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
}
try {
socket2.send(packet2);
} catch (IOException e) {
e.printStackTrace();
}
//关闭资源
socket.close();
socket2.close();
总结:
- 是否关闭输出流和输入流
- 对于同一个socket,如果关闭了输出流,则与改输出流相关的socket也会被关闭,所以一般不关闭流,直接关闭socket即可
- 传入传出对象、文件---->输入输出流