网络编程
网络编程就是在两个或两个以上的设备之间传输数据。程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这就是狭隘的网络编程范畴。传输数据时,利用程序设计语言的API(大部分都提供网络编程接口,如C#,JAVA)就可以.
1.网络概述
计算机网络是通过一定的物理设备将处于不同位置的计算机连接起来组成的网络。包括计算机、路由器、交换机等设备。
路由器和交换机是计算机网络的核心,计算机只是网络上的节点,通过光线、网线等设备连接起来。网络最主要的优势在于共享:共享设备和共享数据。共享设备即局域网中多台计算机共享一个网络打印机。共享数据如Web应用中用户从同一个数据库服务器获取数据。
网络中的每一个设备都有一个唯一标识其身份的数字标识——IP地址,IPv4和IPv6。
对应IP地址的是域名(Domain Name),方便记忆访问。IP地址和域名是1对多的关系,二者之间通过DNS服务器实现解析,完成域名和IP地址的映射。用户在浏览器输入域名,浏览器首先请求DNS服务器将域名转换为IP地址,然后将转换后的IP地址传给浏览器,根据地址就行数据传输。
另外网络中数据传输还需要网络协议,它规范数据传输的标准,从而在数据发送方和数据接收方实现传输。TCP(传输控制协议,可靠传输)和UDP(用户数据包协议,不可靠传输) 。TCP,连接时需要建立专门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则客户端会重新发送该数据,类似电话。UDP不需要建立专门的虚拟连接,传输也就不可靠,如果发送失败则无法获得,类似短信。
网络通讯基于“请求-响应”模型,一方请求数据,另一方响应请求并反馈数据。第一次主动发起通讯的程序称作客户端(Client)程序,第一次通讯中等待连接的程序被称作服务器端(Server)程序。两种请求-响应模型:C/S(客户端/服务器),B/S(浏览器/服务器)。C/S需开发客户端和服务器端,表现力丰富,通用性差、维护要求高。B/S只需开发服务器端,开发速度快、维护压力小,缺点是表现力差、无法进行系统级操作。
另一一种特殊的是P2P(Point to Point)程序。一个P2P程序既包括客户端程序,也包括服务器程序,常见的有BT、电驴等。使用客户端程序部分连接其它的种子(服务器端),而使用服务器端向其它的BT客户端传输数据。
2.网络编程技术
2.1网络编程步骤
无论使用TCP方式还是UDP方式进行网络通讯,网络编程都是由客户端和服务器端组成。B/S只需要实现服务器端即可,下面以C/S结构讲述。
2.1.1客户端网络编程步骤
客户端是网络通讯中发起连接的程序,编程实现包括3个步骤:
1.建立网络连接
客户端编程的第一步是建立网络连接,在建立网络连接时需要指定连接到的服务器的IP地址和端口号,建立完成后会形成一跳虚拟的连接。
2.交换数据
交换数据按照请求响应模型进行,如客户端发送一个请求数据到服务器;服务器反馈一个响应数据给客户端,如果客户端不发生则服务器端不响应。
根据逻辑,可以进行多次交换,但必须遵循请求响应模型。即先发送后接收。
3.关闭网络连接
数据交换完成后,关闭网络连接,释放程序占用的端口、内存等系统资源,结束网络编程。
建立网络连接、交换数据、关闭网络连接。交换数据会进行多次,每次数据交换会开辟一个线程专门进行。
2.1.2服务器端网络编程步骤
服务器端(Server)指在网络编程中被动等待连接的程序,服务器端一般实现程序的核心逻辑以及数据存储等核心功能。服务器端的编程步骤和客户端不同,由4个步骤组成。
1.监听端口
服务器监听程序固定端口,被动等待连接,程序端口即服务器事先开放给客户端的端口。
2.获得连接
当客户端连接到服务器端时,服务器端就可以获得一个连接,这个连接包含客户端的信息(IP,端口)。服务器端和客户端通过该连接进行数据交换。开启专门的线程处理该连接,每个连接都由独立的线程实现。
3.交换数据
服务器端通过获得的连接进行数据交换。先接收客户端发送的数据,然后逻辑处理,最后反馈处理结果,即先接收后发送。
4.关闭连接
关闭开启的连接。
2.1.3Java网络编程技术
Java中与网络编程有关的API位于java.net中,定义了基本的网络编程实现。
InetAddress类,该类代表一个IP地址,将IP地址和域名相关的操作方法封装在该类内部。该类操作方法如下:
1.使用域名创建对象。 InetAddress inet1=InetAddress.getByName("www.baidu.com");
2.使用IP地址创建对象。InetAddress inet2=InetAddress.getByName("127.0.0.1");
3.获得本机地址对象。 InetAddress inet3=InetAddress.getLocalHost();
4.获得对象中存储的域名。String host=inet3.getHostName();
5.获得对象中存储的IP。String ip=inet3.getHostAddress();
2.2TCP/UDP编程实现
2.2.1TCP 编程
java.net.Socket类代表客户端连接,java.net.ServerSocket类代表服务器端连接。封装底层网络通讯,使用时指定IP和端口就可以建立连接。
1.建立连接
Socket socket1=new Socket("192.168.0.1,10000)
Socket socket2=new Socket("www.baidu.com",10000)
2.发送数据
按照请求响应模型进行网络数据交换。通过Java IO实现,从连接中获得输入流和输出流,将需要发送的数据写入连接对象的输出流中,在发送完后从输入流中读取数据即可。
OutputStream os=socket1.getOutputStream();//获得输出流
InputStream in=socket1.getInputStream();//获得输入流
连接建立,从连接中获得输入流、输出流,然后先向服务器写出,后从服务器发送过来的数据中读入,完成客户端先写后读。
监听端口,连接建立,从连接中获得输入、输出流,先从输入中获得客户端输入,后向输出中写入服务器的反馈信息,完成服务器端先读后写。
3.关闭连接
socket.close();in.close();,out.close();serverSocket.close();
4.关于监听端口
ServerSocket ss=new ServerSocket(10000);
socket socket=ss.accept();
下面简单实现一个网络客户端程序,客户端向服务器端发送一个字符串“Hello”,并将服务器端反馈输出到控制台,数据交换进行3次,交换完成后关闭。
客户端:
package Socker_Pro;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class review_MultiSocketClient {
public static void main(String[] args){
Socket socket=null;
InputStream inputStream=null;
OutputStream outputStream=null;
String serverIp="127.0.0.1";
int serverPort=10000;
String helloString="hello";
try{
//连接初始化
InetAddress address=InetAddress.getByName(serverIp);
socket=new Socket(address, serverPort);
//数据发送。
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class review_MultiSocketClient {
public static void main(String[] args){
Socket socket=null;
InputStream inputStream=null;
OutputStream outputStream=null;
String serverIp="127.0.0.1";
int serverPort=10000;
String helloString="hello";
try{
//连接初始化
InetAddress address=InetAddress.getByName(serverIp);
socket=new Socket(address, serverPort);
//数据发送。
//在for循环中操作输入输出流,可以完成多次发送数据
byte[] sendData=helloString.getBytes();
outputStream=socket.getOutputStream();
outputStream.write(sendData);
System.out.println("客户端发送数据:Hello");
//数据接收
byte[] responseData=new byte[1024];
inputStream=socket.getInputStream();
int n=inputStream.read(responseData);
System.out.println("接收到服务器反馈信息:"+new String(responseData,0,n));
}catch (Exception e) {
}finally{
try{
inputStream.close();
outputStream.close();
socket.close();
}catch (Exception e) {
}
}
}
}
byte[] sendData=helloString.getBytes();
outputStream=socket.getOutputStream();
outputStream.write(sendData);
System.out.println("客户端发送数据:Hello");
//数据接收
byte[] responseData=new byte[1024];
inputStream=socket.getInputStream();
int n=inputStream.read(responseData);
System.out.println("接收到服务器反馈信息:"+new String(responseData,0,n));
}catch (Exception e) {
}finally{
try{
inputStream.close();
outputStream.close();
socket.close();
}catch (Exception e) {
}
}
}
}
服务器端:
package Socker_Pro;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class review_MultiSocketServer {
public static void main(String[] args){
ServerSocket ss=null;
Socket socket=null;
I nputStream inputStream=null;
OutputStream outputStream=null;
int port=10000;
String message="fine, thank you";
try{
//监听端口
ss=new ServerSocket(port);
//建立连接
socket=ss.accept();
//接收数据
byte[] receivedData=new byte[1024];
inputStream=socket.getInputStream();
int n=inputStream.read(receivedData);
System.out.println("server has received:"+new String(receivedData,0,n));
//发送数据
outputStream=socket.getOutputStream();
outputStream.write(message.getBytes(), 0, message.getBytes().length);
}catch (Exception e) {
}finally{
try{
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class review_MultiSocketServer {
public static void main(String[] args){
ServerSocket ss=null;
Socket socket=null;
I nputStream inputStream=null;
OutputStream outputStream=null;
int port=10000;
String message="fine, thank you";
try{
//监听端口
ss=new ServerSocket(port);
//建立连接
socket=ss.accept();
//接收数据
byte[] receivedData=new byte[1024];
inputStream=socket.getInputStream();
int n=inputStream.read(receivedData);
System.out.println("server has received:"+new String(receivedData,0,n));
//发送数据
outputStream=socket.getOutputStream();
outputStream.write(message.getBytes(), 0, message.getBytes().length);
}catch (Exception e) {
}finally{
try{
outputStream.close();
inputStream.close();
socket.close();
ss.close();
}catch (Exception e) {
}
}
}
}
}catch (Exception e) {
}
}
}
}
实际中多客户端访问,为每一个客户端连接建立一个线程,进行数据处理。每次使用一个连接对象构造该线程,该连接对象就是该线程需要处理的连接,在线程构造完成以后,该线程就被启动起来了,然后在run()方法内部对客户端连接进行处理,读发送数据,写反馈数据。将客户端发送过来的数据处理封装了logic()方法。
实际中不能无限开启线程,且频繁创建线程效率低下,因此采用线程池的技术,程序启动时首先把需要的线程对象创建好,如1000个线程对象,当客户端连接时从池中去除一个已经创建好的线程对象即可。当客户端连接关闭后,将该线程对象重新放入线程池中。
package Socker_Pro;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class review_MultiThreadSocketServer {
public static void main(String[] args){
ServerSocket ss=null;
Socket socket=null;
try{
ss=new ServerSocket(10000);
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class review_MultiThreadSocketServer {
public static void main(String[] args){
ServerSocket ss=null;
Socket socket=null;
try{
ss=new ServerSocket(10000);
//等待连接建立
while(true){
socket=ss.accept();
new SocketThread(socket);
}}catch (Exception e) {
}finally{
try{
ss.close();
}catch (Exception e) {
}
}
}
}
//线程类
class SocketThread extends Thread{
Socket socket;
InputStream inputStream;
OutputStream outputStream;
public SocketThread(Socket socket){
this.socket=socket;
start();
}
public void run(){
byte[] data=new byte[1024];
try{
inputStream=socket.getInputStream();
outputStream=socket.getOutputStream();
for(int i=0;i<3;i++){
int n=inputStream.read(data);
//逻辑处理
byte[] response=logic(data,0,n);
outputStream.write(response);
}
}catch (Exception e) {
}finally{
try{
outputStream.close();
inputStream.close();
socket.close();
}catch (Exception e) {
}
}
}
//输入到输出数组转换
public byte[] logic(byte[] d,int m,int n){
byte[] response=new byte[n];
//拷贝数组
System.arraycopy(d, m, response, m, n);
return response;
}
}
while(true){
socket=ss.accept();
new SocketThread(socket);
}}catch (Exception e) {
}finally{
try{
ss.close();
}catch (Exception e) {
}
}
}
}
//线程类
class SocketThread extends Thread{
Socket socket;
InputStream inputStream;
OutputStream outputStream;
public SocketThread(Socket socket){
this.socket=socket;
start();
}
public void run(){
byte[] data=new byte[1024];
try{
inputStream=socket.getInputStream();
outputStream=socket.getOutputStream();
for(int i=0;i<3;i++){
int n=inputStream.read(data);
//逻辑处理
byte[] response=logic(data,0,n);
outputStream.write(response);
}
}catch (Exception e) {
}finally{
try{
outputStream.close();
inputStream.close();
socket.close();
}catch (Exception e) {
}
}
}
//输入到输出数组转换
public byte[] logic(byte[] d,int m,int n){
byte[] response=new byte[n];
//拷贝数组
System.arraycopy(d, m, response, m, n);
return response;
}
}
2.2.2UDP编程
必须要求可靠传输的信息一般使用TCP方式实现,一般的数据才使用UDP方式实现。
Java API 中,UDP编程涉及两个类:
1.DatagramSocket
DatagramSocket类实现“网络连接”,包括客户端连接和服务器端网络连接。
客户端构建该连接对象,
客户端直接无参构造,DatagramSocket ds=new DatagramSocket();
服务器根据监听端口构造,DatagramSocket ds=newDatagramSocket(port);
DatagramSocket有send(DatagramPacket dp)和receive(DatagramPacket dp)方法,分别用来发送和接收数据包对象。
2.DatagramPacket
DatagramPacket类实现对于网络传输中的数据封装,即类的对象代表网络中交换的数据。UDP中,发送和接收的数据都必须被封装成DatagramPacket对象。该对象包含数据内容、发送到的IP、发送到的端口。其中数据必须转换为数组做为参数,第二个参数为数组长度,第三个为InetAddress对象的IP,第四个为端口。
发送数据时,DatagramPacket dp=new DatagramPacket(byte[] content, byte_length, inetAddress,port);数据数组、长度,IP,端口。
接收数据时,DatagramPacket dp=new DatagramPacket(byte[] void, byte_length);空数组和长度接收数据。
DatagramPacket类存储数据内容、长度、IP和端口。通过下列方法获得。
byte[] content=dp.getData(); int length=dp.getLength(); InetAddress address=dp.getAddress(); int port=dp.getPort();
客户端编程过程:建立连接、数据传输(发送、接收)、关闭连接。
服务器端编程过程:监听端口(等效完成连接建立)、数据传输(接收、发送)、关闭连接。
客户端 编程实现:
package Socket_UDP;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Date;
public class review_UDPClient {
public static void main(String[] args){
DatagramSocket ds=null;
DatagramPacket sendDp=null;
DatagramPacket receivedDp=null;
String serverIp="127.0.0.1";
int port=10001;
try{
//建立连接
ds=new DatagramSocket();
//初始化发送对象
Date date=new Date();
String content=date.toString();
InetAddress address=InetAddress.getByName(serverIp);
byte[] dataout=content.getBytes();
sendDp=new DatagramPacket(dataout, dataout.length,address,port);
//发送数据对象
ds.send(sendDp);
//初始化接收对象
byte[] datain=new byte[1024];
receivedDp=new DatagramPacket(datain, datain.length);
//接收数据对象
ds.receive(receivedDp);
//读取内容
byte[] response=receivedDp.getData();
int length=receivedDp.getLength();
String string=new String(response,0,length);
System.out.println(string);
}catch (Exception e) {
}finally{
try{
ds.close();
}catch (Exception e) {
}
}
}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Date;
public class review_UDPClient {
public static void main(String[] args){
DatagramSocket ds=null;
DatagramPacket sendDp=null;
DatagramPacket receivedDp=null;
String serverIp="127.0.0.1";
int port=10001;
try{
//建立连接
ds=new DatagramSocket();
//初始化发送对象
Date date=new Date();
String content=date.toString();
InetAddress address=InetAddress.getByName(serverIp);
byte[] dataout=content.getBytes();
sendDp=new DatagramPacket(dataout, dataout.length,address,port);
//发送数据对象
ds.send(sendDp);
//初始化接收对象
byte[] datain=new byte[1024];
receivedDp=new DatagramPacket(datain, datain.length);
//接收数据对象
ds.receive(receivedDp);
//读取内容
byte[] response=receivedDp.getData();
int length=receivedDp.getLength();
String string=new String(response,0,length);
System.out.println(string);
}catch (Exception e) {
}finally{
try{
ds.close();
}catch (Exception e) {
}
}
}
}
服务器端编程:
package Socket_UDP;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class review_UDPServer {
public static void main(String[] args){
DatagramSocket ds=null;
DatagramPacket sendDp=null;
DatagramPacket receivedDp=null;
try{
//监听端口
ds=new DatagramSocket(10001);
//初始化接收数据
byte[] datain=new byte[1024];
receivedDp=new DatagramPacket(datain, datain.length);
//接收数据
ds.receive(receivedDp);
//读取数据
byte[] datain1=receivedDp.getData();
int length=receivedDp.getLength();
InetAddress address=receivedDp.getAddress();
int port=receivedDp.getPort();
System.out.println("server has receive "+new String(datain1,0,length));
//初始化发送数据
byte[] dataout="time has received".getBytes();
sendDp=new DatagramPacket(dataout, dataout.length,address,port);
ds.send(sendDp);
}catch (Exception e) {
}finally{
try{
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class review_UDPServer {
public static void main(String[] args){
DatagramSocket ds=null;
DatagramPacket sendDp=null;
DatagramPacket receivedDp=null;
try{
//监听端口
ds=new DatagramSocket(10001);
//初始化接收数据
byte[] datain=new byte[1024];
receivedDp=new DatagramPacket(datain, datain.length);
//接收数据
ds.receive(receivedDp);
//读取数据
byte[] datain1=receivedDp.getData();
int length=receivedDp.getLength();
InetAddress address=receivedDp.getAddress();
int port=receivedDp.getPort();
System.out.println("server has receive "+new String(datain1,0,length));
//初始化发送数据
byte[] dataout="time has received".getBytes();
sendDp=new DatagramPacket(dataout, dataout.length,address,port);
ds.send(sendDp);
}catch (Exception e) {
}finally{
try{
ds.close();
}catch (Exception e) {
}
}
}
}
}catch (Exception e) {
}
}
}
}