网络编程时代几乎是结束了,主要目的是为了阐述网络擦欧做的过程,因为这些过程,将直接影响到Java EE的项目开发
1. 网络编程(简介)
网络:是将物理上分开的主机进行连接所形成的交换区域
所谓的网络编程指的就是服务器端与客户端编程的开发操作实现。但是在我们实际的工作下对网络的编程有两种形式:
- 形式一:C/S结构(Client/Server)此类模式的开发一般要编写两套程序,一端属于客户端代码,一端是服务器代码,这样程序的开发非常的麻烦,因为我们要维护两套程序的使用,但是这类程序有一点好处就是安全性高。因为使用的是自己的连接端口和通讯协议。
- 形式二:B/S结构(Browser/Server)不再单独开发客户端代码,只开发一套服务器端程序,客户端将利用浏览器进行访问,这种模式只需要开发一套程序,因为使用的是一套公共的HTTP协议以及公共的80端口,安全性不高,所以做的时候会对数据进行加密处理。
本篇指的是C/S开发,这样的程序也称为Socket程序,对于C/S结构的程序分为两类:
- TCP程序:是采用可靠的连接方式进行的传输
- UDP程序:不可靠的连接,属于数据报的协议
2.网络编程(基本实现)
如果要进行网络程序的开发,那么最为核心的两个类:
- 服务器类:ServerSocket,主要工作在服务器端用于接收工作的请求
- 客户端类:Socket, 每一个连接到服务器上的用户都通过Socket表示
定义服务器端——主要使用ServerSocket
- 构造方法:
public ServerSocket(int port)throws IOException
,设置监听端口;(把所有端口都关闭了,电脑是最安全的,所谓的防火墙也是针对端口进行拦截) - 接受客户端连接:
public Socket accept()throws IOException
, - 取得客户端的输出功能:Socket类定义方法
public InputStream getInputStream()throws IOException
和public OutputStreamgetOutputStream()throws IOException
客户端的输入信息和客户端的输出信息
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Main {//主类
public static void main(String args[]) throws Exception {
ServerSocket server = new ServerSocket(9999);
//所有的服务器必须有端口
System.out.println("等待客户端连接");
Socket client= server.accept();//等待客户端连接,如果没有连接上一直等待
//OutputStream并不方便输出,所以利用打印流输出
PrintStream out = new PrintStream(client.getOutputStream());
out.println("Hello World!");//不加换行Scanner就可能读不到了
out.close();
client.close();
server.close();
}
}
此时的服务器端只是输出一个HelloWorld的字符串,然后就关闭服务器操作只能够处理一次客户端的请求。
编写客户端——主要使用Socket
- 构造方法:
public Socket(String host, int port) throws UnknownHostException,IOException
接收主机:就是你的IP地址,如果是本机直接访问,那么使用localhost(127.0.0.1)代替IP,后面是端口 - 得到输入数据:
public InputStream getInputStream() throws IOException
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Scanner;
public class HelloClient {
public static void main(String args[])throws Exception{
Socket client =new Socket("localhost",9999);
//表示连接服务器端了
//取得客户端的输入数据流对象,表示接收服务器端的输出信息
Scanner scan = new Scanner(client.getInputStream());
scan.useDelimiter("\n");
if(scan.hasNext()){
System.out.println("回应数据:"+scan.next());
}
scan.close();
client.close();
}
}
客户端现在也只是连接一次服务器,并且接受输入数据输出后结束操作
在终端执行HelloClient之后,原来一直wait的服务器端口结束执行,客户端输出Hello World!
这是一个最简单的C/S模型,要想连接光有主机不够还要有端口,Socket是客户端。
3.网络编程(Echo模型)
在网络编程之中ECHO是一个经典的程序开发模型,本程序的意义在于:客户端随意输入信息,并且将信息发送给服务器端,服务器端接收后,前面加一个“ECHO:”的标记返回。
在中端也有一个Echo操作,输入什么输出什么。
本程序设计如下:
- 由于需要采用多次输入的模式,所以不能够每次连接后就立刻关闭服务器端
- 可以设置一个字符串,eg:byebye,那么才表示结束本次的操作
实现服务器端:
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.ServerSocketChannel;
import java.rmi.ServerError;
import java.util.Scanner;
public class Main {//主类
public static void main(String args[]) throws Exception {
ServerSocket server = new ServerSocket(9999);
Socket client = server.accept();//连接客户端
//客户端输入数据以及向客户端输出数据的对象
Scanner scan = new Scanner(client.getInputStream());//
PrintStream out = new PrintStream(client.getOutputStream());//向客户端输出信息
boolean flag=true;//控制多次接收操作
while (flag){
if(scan.hasNext())
{
String str = scan.next().trim();//得到客户端发送的内容,trim忽略前后空格
//但是问题是它发送的内容有可能有空格
if(str.equalsIgnoreCase("byebye")){//表示程序要结束
out.println("see you next time");
flag = false;//表示退出这个循环
}
else {
out.println("ECHO:"+str);
}
}
}
client.close();//客观来讲把客户端关闭实际上就把所有的都关闭了
server.close();
scan.close();
out.close();
}
}
服务器端实现了循环操作
实现客户端
import java.io.PrintStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Scanner;
public class HelloClient {
public static void main(String args[])throws Exception{
Socket client =new Socket("localhost",9999);
//表示连接服务器端了
//取得客户端的输入数据流对象,表示接收服务器端的输出信息
Scanner input = new Scanner(System.in);//键盘输入数据
input.useDelimiter("\n");
Scanner scan = new Scanner(client.getInputStream());
scan.useDelimiter("\n");
PrintStream out = new PrintStream(client.getOutputStream());
boolean flag = true;
while(flag){
System.out.print("请输入要发送数据:");
if(input.hasNext()){
String str = input.next().trim();
out.println(str);//向服务器端发送数据,无论什么数据都要给服务器端发送
if(str.equalsIgnoreCase("byebye")){
flag = false;//结束循环
}
else {
if(scan.hasNext())
System.out.println(scan.next());//输出回应数据
}
}
}
scan.close();
client.close();
scan.close();
input.close();
out.close();
}
}
此时再开一个终端无法运行,就实现了一个最简单的服务器端与客户端通讯!
此时只能连接一个客户端,因为我们所有的操作都是在主线程上进行的开发,此时的程序属于单线程的网络应用,世纪中不可能如此进行,所以为了让一个服务器端能够为多个客户端服务,使用多线程描述。把每个连接到服务器端的客户都作为一个独立的线程对象保留。
服务器端
package com.company;
import javax.print.attribute.standard.OrientationRequested;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.ServerSocketChannel;
import java.rmi.ServerError;
import java.util.Scanner;
public class Main {//主类
public static void main(String args[]) throws Exception {
ServerSocket server = new ServerSocket(9999);
Socket client = server.accept();//连接客户端
//客户端输入数据以及向客户端输出数据的对象
Scanner scan = new Scanner(client.getInputStream());//
PrintStream out = new PrintStream(client.getOutputStream());//向客户端输出信息
boolean flag=true;//控制多次接收操作
while (flag){
if(scan.hasNext())
{
String str = scan.next().trim();//得到客户端发送的内容,trim忽略前后空格
//但是问题是它发送的内容有可能有空格
if(str.equalsIgnoreCase("byebye")){//表示程序要结束
out.println("see you next time");
flag = false;//表示退出这个循环
}
else {
out.println("ECHO:"+str);
}
}
}
client.close();//客观来讲把客户端关闭实际上就把所有的都关闭了
server.close();
scan.close();
out.close();
}
}
客户端
import java.io.PrintStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Scanner;
public class HelloClient {
public static void main(String args[])throws Exception{
Socket client =new Socket("localhost",9999);
//表示连接服务器端了
//取得客户端的输入数据流对象,表示接收服务器端的输出信息
Scanner input = new Scanner(System.in);//键盘输入数据
Scanner scan = new Scanner(client.getInputStream());
input.useDelimiter("\n");
scan.useDelimiter("\n");
PrintStream out = new PrintStream(client.getOutputStream());
boolean flag = true;
while(flag){
System.out.print("请输入要发送数据:");
if(input.hasNext()){
String str = input.next().trim();
out.println(str);//向服务器端发送数据,无论什么数据都要给服务器端发送
if(str.equalsIgnoreCase("byebye")){
flag = false;//结束循环
}
if(scan.hasNext())
System.out.println(scan.next());//输出回应数据
}
}
input.close();
scan.close();
out.close();
client.close();
}
}
但是现在面临着一个问题就是如果我输出what are中间有空格的话,他就会把数据保存在流中,然后一个一个的输出,比如上面的输出,what are输出之后,会先返回what但是你第二次再次输入一个数据他返回的就是are而不是你第二次输入的数据,因为在一开始写的时候就是简单的只能是一个单词的形式。
下面的是多线程写法:
修改服务器端
package com.company;
import javax.print.attribute.standard.OrientationRequested;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.ServerSocketChannel;
import java.rmi.ServerError;
import java.util.Scanner;
class EchoThread implements Runnable{
private Socket client;
public EchoThread(Socket client){
this.client=client;
}
@Override
public void run() {
try{//客户端输入数据以及向客户端输出数据的对象
Scanner scan = new Scanner(client.getInputStream());//
PrintStream out = new PrintStream(client.getOutputStream());//向客户端输出信息
boolean flag=true;//控制多次接收操作
while (flag){
if(scan.hasNext())
{
String str = scan.next().trim();//得到客户端发送的内容,trim忽略前后空格
//但是问题是它发送的内容有可能有空格
if(str.equalsIgnoreCase("byebye")){//表示程序要结束
out.println("see you next time");
flag = false;//表示退出这个循环
}
else {
out.println("ECHO:"+str);
}
}
}
scan.close();
out.close();
client.close();}
catch (Exception e){
e.printStackTrace();
}
}
}
public class Main {//主类
//每一个客户端进来之后都启动一个专门的线程进行此客户端的处理,此时的服务器端就可以支持多个用户的访问
public static void main(String args[]) throws Exception {
ServerSocket server = new ServerSocket(9999);
boolean flag = true;
while (flag) {
Socket client = server.accept();//连接客户端
new Thread(new EchoThread(client)).start();
}
//client.close();//客观来讲把客户端关闭实际上就把所有的都关闭了
server.close();
}
}
服务器开发的基础要素:网络支持类、IO、多线程。
还要考虑很多情况比如线程间的通讯、资源同步(死锁)
总结
很少会进行实际开发了,所以了解一下基本概念就可以了,但至少可以通过代码看出多线程的特点和好处