网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。
java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。
java.net 包中提供了两种常见的网络协议的支持:
TCP:TCP 是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。
UDP:UDP 是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。
一.InetAddress 类一个基础的类
该类的功能是代表一个IP地址,并且将IP地址和域名相关的操作方法包含在该类的内部。下面是socket编程常用到的方法:
二.网络编程的步骤
按照前面的基础知识介绍,无论使用TCP方式还是UDP方式进行网络通讯,网络编程都是由客户端和服务器端组成
客户端网络编程步骤
客户端(Client)是指网络编程中首先发起连接的程序,客户端一般实现程序界面和基本逻辑实现,在进行实际的客户端编程时,无论客户端复杂还是简单,以及客户端实现的方式,客户端的编程主要由三个步骤实现:
1、 建立网络连接
客户端网络编程的第一步都是建立网络连接。在建立网络连接时需要指定连接到的服务器的IP地址和端口号,建立完成以后,会形成一条虚拟的连接,后续的操作就可以通过该连接实现数据交换了。
2、 交换数据
连接建立以后,就可以通过这个连接交换数据了。交换数据严格按照请求响应模型进行,由客户端发送一个请求数据到服务器,服务器反馈一个响应数据给客户端,如果客户端不发送请求则服务器端就不响应。
根据逻辑需要,可以多次交换数据,但是还是必须遵循请求响应模型。
3、 关闭网络连接
在数据交换完成以后,关闭网络连接,释放程序占用的端口、内存等系统资源,结束网络编程。
最基本的步骤一般都是这三个步骤,在实际实现时,步骤2会出现重复,在进行代码组织时,由于网络编程是比较耗时的操作,所以一般开启专门的现场进行网络通讯。
服务器端网络编程步骤
服务器端(Server)是指在网络编程中被动等待连接的程序,服务器端一般实现程序的核心逻辑以及数据存储等核心功能。服务器端的编程步骤和客户端不同,是由四个步骤实现,依次是:
1、 监听端口
服务器端属于被动等待连接,所以服务器端启动以后,不需要发起连接,而只需要监听本地计算机的某个固定端口即可。
这个端口就是服务器端开放给客户端的端口,服务器端程序运行的本地计算机的IP地址就是服务器端程序的IP地址。
2、 获得连接
当客户端连接到服务器端时,服务器端就可以获得一个连接,这个连接包含客户端的信息,例如客户端IP地址等等,服务器端和客户端也通过该连接进行数据交换。
一般在服务器端编程中,当获得连接时,需要开启专门的线程处理该连接,每个连接都由独立的线程实现。
3、 交换数据
服务器端通过获得的连接进行数据交换。服务器端的数据交换步骤是首先接收客户端发送过来的数据,然后进行逻辑处理,再把处理以后的结果数据发送给客户端。简单来说,就是先接收再发送,这个和客户端的数据交换数序不同。
其实,服务器端获得的连接和客户端连接是一样的,只是数据交换的步骤不同。
当然,服务器端的数据交换也是可以多次进行的。
在数据交换完成以后,关闭和客户端的连接。
4、 关闭连接
当服务器程序关闭时,需要关闭服务器端,通过关闭服务器端使得服务器监听的端口以及占用的内存可以释放出来,实现了连接的关闭。
三.socket通信
套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。
Socket通信实现步骤:
1、创建ServerSocket和Socket
2、打开连接Socket的输入/输出流
3、按照协议(通常是TCP/UDP)对Socket进行读写操作
4、关闭输入/输出流,关闭Socket
1、Server服务器端:
a、创建ServerSocket对象,同时绑定监听端口
b、通过accept()方法监听客户端的请求
c、建立连接后,通过输入流读取客户端发送的请求信息
d、通过输出流向客户端发送响应信息
e、关闭相应资源
package com.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
/**
* 基于TCP协议的服务端Socket通信
* 服务器端必须早于客户端启动
* @author feizi
*
*/
public class ServerSocketDemo {
public static void main(String[] args) {
try {
//1、创建一个服务端Socket,即ServerSocket对象,指定绑定的端口,并侦听该端口
ServerSocket serverSocket = new ServerSocket(5555);
//2、调用accept()方法开始侦听客户端请求,创建Socket,等待客户端的连接
System.out.println("===================服务器即将启动,等待客户端的连接===============");
Socket socket = serverSocket.accept();
//3、获取输入字节流,读取客户端请求信息
InputStream is = socket.getInputStream();
//将字节流包装成字符流
InputStreamReader isr = new InputStreamReader(is);
//为字符输入流添加缓冲
BufferedReader br = new BufferedReader(isr);
//读取字符输入流中的数据信息
String data = null;
while(null != (data = br.readLine())){
System.out.println(new Date());
System.out.println("我是服务器端,客户端说:"+data);
}
//调用shutdown方法关闭输入流
socket.shutdownInput();
//4、获取输出字节流,响应客户端的信息
OutputStream os = socket.getOutputStream();
//将字节流包装成为字符打印流
PrintWriter pw = new PrintWriter(os);
//向客户端回复响应消息
pw.write("用户名和密码输入正确");
//刷新缓存
pw.flush();
//关闭socket输出流
socket.shutdownOutput();
//5、关闭资源
pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、Client客户端:
a、创建Socket对象,指明需要连接的服务器的地址和端口号
b、建立连接后,通过输出流向服务器端发送请求信息
c、通过输入流获取服务器的响应信息
d、关闭相应资源
package com.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
/**
* 基于TCP协议的客户端Socket通信
* @author feizi
*
*/
public class ClientSocketDemo {
public static void main(String[] args) {
try {
//1、创建客户端Socket,指定服务器地址和端口号,向服务端发送请求信息
Socket socket = new Socket("localhost", 5555);
//2、获取输出字节流,向服务器端发送消息
OutputStream os = socket.getOutputStream();
//3、将字节输出流包装为字符打印流
PrintWriter pw = new PrintWriter(os);
//向服务器端发送请求信息
StringBuffer bf = new StringBuffer();
bf.append("用户名:").append("admin");
bf.append("密码:").append("123");
pw.write(bf.toString());
//刷新缓存
pw.flush();
//关闭Socket的输出流
socket.shutdownOutput();
//3、获取输入字节流,读取服务器端的响应信息
InputStream is = socket.getInputStream();
//将输入字节流包装成字符字符流
InputStreamReader isr = new InputStreamReader(is);
//为字符流添加缓冲区
BufferedReader br = new BufferedReader(isr);
//通过服务器端的响应信息
String data = null;
while(null != (data=br.readLine())){
System.out.println(new Date());
System.out.println("我是客户端,服务器端说:"+data);
}
//关闭Socket输入流
socket.shutdownInput();
//4、关闭资源
br.close();
isr.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
四.socket实现多个客户端与服务端通信
实际应用中单个客户端通信的情况几乎是不存在的,一般要多个客户端与服务端通信,需要服务器端不停的侦听客户端的请求(写在while循环中,并且条件始终为true,死循环),每当侦听到一个客户端的请求时,都需要创建一个Socket与之建立通信(通过线程实现,每当侦听到一个客户端的请求,服务端都要单独开辟一条线程与之进行通信)。
1、服务端Socket,(这里面通过死循环让服务器端一直循环侦听来自客户端的请求)
需要注意的是,服务端必须要先于客户端启动,因为要启动之后才能侦听客户端的请求
package com.socket;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器端Socket
* @author feizi
* @time 2014-11-14下午5:08:16
*/
public class ServerSocketMany {
/**
* main测试方法
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException{
System.out.println("服务器已经启动,等待客户端的连接....");
//创建服务器端Socket,即ServerSOcket,等待客户端的请求
ServerSocket server = new ServerSocket(5555);
int count = 0;//侦听到的客户端的数量
Socket socket = null;//服务器端Socket
//死循环,让服务端循环侦听
while(true){
//服务端开始侦听客户端的连接
socket = server.accept();
//启动线程,与客户端进行通信
Thread serverThread = new ServerThread(socket);
serverThread.start();
//计数,统计客户端连接数
count++;
System.out.println("当前链接的客户端的数量为:"+count+"个....");
}
}
}
2、服务器端的线程实现类,线程中主要是实现与客户端的通信(通过输入输出流接收并响应数据信息)
package com.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
/**
* 创建一个服务端线程,循环侦听客户端的请求,多个客户端
* @author feizi
* @time 2014-11-14下午5:36:45
*/
public class ServerThread extends Thread{
//每当侦听到一个新的客户端的时,服务端这边都要有一个Socket与之进行通信
public Socket socket = null;
//默认的构造方法,保留
public ServerThread(){}
//带参构造方法
public ServerThread(Socket socket){
this.socket = socket;
}
//覆写run方法
public void run(){
//获取输入字节流
InputStream in = null;
//将输入字节流包装成输入字符流
InputStreamReader isr = null;
//为字符输入流添加缓冲
BufferedReader br = null;
//收到信息之后,向客户端响应信息,获取输出字节流
OutputStream out = null;
//将字节输出流包装成字符打印输出流
PrintWriter pw = null;
try {
in = socket.getInputStream();
isr = new InputStreamReader(in);
br = new BufferedReader(isr);
//读取字符输入流中的数据
String data = null;
while((data = br.readLine()) != null){
System.out.println("我是服务器,客户端说:"+data);
}
//调用shutDown方法关闭Socket输入流
socket.shutdownInput();
out = socket.getOutputStream();
pw = new PrintWriter(out);
pw.write("用户名和密码正确");
pw.flush();
//调用shutDown方法关闭Socket输出流
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}finally{
//关闭资源
try {
if(null != pw)
pw.close();
if(null != out)
out.close();
if(null != br)
br.close();
if(null != isr)
isr.close();
if(null != in)
in.close();
if(null != socket)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端的代码与上面的相同,调整的是服务端的部分
参考:http://blog.csdn.net/csh624366188/article/details/7331716
http://www.runoob.com/java/java-networking.html
http://blog.csdn.net/hu1991die/article/details/40679605