在正式进入网络编程之前先简单介绍一个网络编程的基本概念。
网络编程就是两个或多个设备之间的数据交换,其实更具体的说,网络编程就是两个或多个程序之间的数据交换即可以理解为一次网络通讯过程。在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。
由此,网络编程中的两种程序就分别是客户端和服务器端,例如QQ程序,每个QQ用户安装的都是QQ客户端程序,而QQ服务器端程序则运行在腾讯公司的机房中,为大量的QQ用户提供服务。这种网络编程的结构被称作客户端/服务器结构,也叫做Client/Server结构,简称C/S结构。而一般如J2EE等web开发,则是浏览器/服务器,简称B/S结构。
使用B/S结构的程序,在开发时只需要开发服务器端即可,这种结构的优势在于开发的压力比较小,不需要维护客户端。但是这种结构也存在着很多不足,例如浏览器的限制比较大,表现力不强,无法进行系统级操作等。
总之C/S结构和B/S结构是现在网络编程中常见的两种结构,B/S结构其实也就是一种特殊的C/S结构。
使用B/S结构的程序,在开发时只需要开发服务器端即可,这种结构的优势在于开发的压力比较小,不需要维护客户端。但是这种结构也存在着很多不足,例如浏览器的限制比较大,表现力不强,无法进行系统级操作等。
总之C/S结构和B/S结构是现在网络编程中常见的两种结构,B/S结构其实也就是一种特殊的C/S结构。
最后再介绍一个网络编程中最重要,也是最复杂的概念——协议(Protocol)。按照前面的介绍,网络编程就是运行在不同计算机中两个程序之间的数据交换。在实际进行数据交换时,为了让接收端理解该数据,计算机比较笨,什么都不懂的,那么就需要规定该数据的格式,这个数据的格式就是协议。
那么如何来编写协议格式呢?答案是随意。只要按照这种协议格式能够生成唯一的编码,按照该编码可以唯一的解析出发送数据的内容即可。也正因为各个网络程序之间协议格式的不同,所以才导致了客户端程序都是专用的结构。
在实际的网络程序编程中,最麻烦的内容不是数据的发送和接收,因为这个功能在几乎所有的程序语言中都提供了封装好的API进行调用,最麻烦的内容就是协议的设计以及协议的生产和解析,这个才是网络编程中最核心的内容。
在现有的网络中,网络通讯的方式主要有两种:
1、 TCP(传输控制协议)方式
2、 UDP(用户数据报协议)方式
关于这两个协议的区别这里不过多介绍。这里我们采用TCP来进行传输。
在客户端网络编程中,首先需要建立连接,在Java API中以java.net.Socket类的对象代表网络连接,所以建立客户端网络连接,也就是创建Socket类型的对象,该对象代表网络连接,示例如下:
Socket socket1 = new Socket(“192.168.1.103”,10000);
Socket socket2 = new Socket(“www.sohu.com”,80);
注:Socket的参数一表示服务器ip地址或者域名或者主机名。(写主机名获取域名最后都被解析为ip地址),参数二表示端口号。
连接一旦建立,则完成了客户端编程的第一步,紧接着的步骤就是按照“请求-响应”模型进行网络数据交换,在Java语言中,数据传输功能由Java IO实现,也就是说只需要从连接中获得输入流和输出流即可,然后将需要发送的数据写入连接对象的输出流中,在发送完成以后从输入流中读取数据即可。示例代码如下:
OutputStream os = socket1.getOutputStream(); //获得输出流
InputStream is = socket1.getInputStream(); //获得输入流
最后当数据交换完成以后,关闭网络连接,释放网络连接占用的系统端口和内存等资源,完成网络操作,示例代码如下:
socket1.close();
现在我们编写一个完整客户段的Demo来向服务器发送字符串 “Hello World”
public class SocketClient {
public static void main(String[] args) {
try{
//创建Socket实例
Socket socket = new Socket("192.168.1.8",8888);
//获取输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,world".getBytes());
//释放资源
outputStream.close();
socket.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
服务端的Demo:
public class SocketService {
public static void main(String[] args) {
InputStream inputStream;
Socket socket;
StringBuilder s = new StringBuilder();
try {
//服务器端接收消息的类。定制端口号为8888
ServerSocket serviceSocket = new ServerSocket(8888);
//获取socket。这个方法是阻塞式的
socket = serviceSocket.accept();
inputStream = socket.getInputStream();
byte buf[] = new byte[1024];
int len = 0;
while((len=(inputStream.read(buf)))>0){
s.append(new String(buf,0,len));
}
//打印客户端的消息
System.out.println(s.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
先运行服务端的Demo,在运行客户端的。即可看到在服务端接受到了客户端传递过来的字符串HelloWorld.
服务端接受到客户端的信息后,向客户端回应一句话:
客户端:
public class SocketClient {
public static void main(String[] args) {
Socket socket =null;
InputStream inputStream =null;
OutputStream outputStream =null;
try{
//创建Socket实例
socket = new Socket("hadoop",10000);
//获取输出流,向服务器发生数据
outputStream = socket.getOutputStream();
outputStream.write("hello world:".getBytes());
///获取输入流,获取服务器的响应
inputStream = socket.getInputStream();
byte[] buff = new byte[1024];
int len = 0;
len = inputStream.read(buff);
//打印服务端的相应
System.out.println(new String(buff,0,len));
}catch(Exception e){
e.printStackTrace();
}finally {
//释放资源
try {
inputStream.close();
outputStream.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
服务端:
public class SocketService {
public static void main(String[] args) {
InputStream inputStream =null;
OutputStream outputStream =null;
Socket socket = null;
ServerSocket serviceSocket =null;
try {
//服务器端接收消息的类。定制端口号为8888
serviceSocket = new ServerSocket(10000);
//获取socket。这个方法是阻塞式的
socket = serviceSocket.accept();
inputStream = socket.getInputStream();
byte buf[] = new byte[1024];
int len = 0;
len =inputStream.read(buf);
//打印客户端的消息
System.out.println(new String(buf,0,len));
//向客户端生成响应
outputStream = socket.getOutputStream();
outputStream.write("收到".getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//释放资源
try {
outputStream.close();
inputStream.close();
socket.close();
serviceSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}