注意:所有文章除特别说明外,转载请注明出处.
概述
我们想要进行网络编程,首先需要服务端通过ServerSocket对某一个端口进行监听。通过accept()来判断是否有客户端与之相连,若成功连上,则通过readline()和println()函数来进行数据的发送和接收,从而实现网络编程。
网络通信协议
HTTP协议:超文本传输协议,互联网应用最广泛的。
FTP协议:文件传输协议。
SMTP协议:简单邮件传输协议。
TCP协议(Transmission Control Protocol 传输控制协议):面向连接的、可靠的、基于字节流的传输通信协议。
UDP协议(User Datagram Protocol用户数据报协议):无连接的协议,在传输数据之前,客户端和服务器并不建立和维护连接,效率很快。
Socket
通信链路的端点就被称为“套接字”(英文名Socket),是提供给应用程序的接口。这里因为Socket的底层机制复杂,Java平台提供了一些简单的API,可以更简单有效的使用Socket开发而无需了解底层机制。
1.Socket分类
1.流式套接字(SOCK_ STREAM)【面向连接、可靠的数据传输服务(对应服务是TCP链接)】
2.数据报式套接字(SOCK_ DGRAM)【无连接服务(数据报似的,基于UDP)更高效】
3.原始式套接字(SOCK_ RAW)
提示:基于TCP协议的Socket网络通信,用来实现双向安全连接网络通信。Socket通信模型,表示在进行网络通信时,Socket需要借助数据流来完成数据的传递工作。
2.Socket网络编程步骤
1.服务端
1.创建ServerSocket(…)对象,绑定某一端口号。
2.通过accept()侦听方法,阻塞,等待对方连接。
3.通过输入、输出流传递信息【客户端是输出输入,服务器是输入输出】。
4.关闭释放资源。
2.客户端
1.创建Socket("…",…)要指定服务器的IP地址,也要指定端口号。
2.发送请求并被accept()侦听到,并建立连接。
3.通过输入、输出流传递信息【客户端是输出输入,服务器是输入输出】。
4.关闭释放资源。
Socket编程
1.直接传参的方式
1.客户端程序
package cn.edu.xidian.see.*.Inet;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
public class LogicClient {
public static void main(String[] args) {
//创建一个客户端的Socket
try {
Socket socket = new Socket("localhost",5000);
//通过输出流发送请求
//直接输出数据流
String info = "用户名:樱桃小丸子;密码:123456";
OutputStream os = socket.getOutputStream();
//打散成数据数组
byte[] infos = info.getBytes();
os.write(infos);
//关闭输出流,这是一个半关闭
socket.shutdownOutput();
//通过输入流接收服务器给我的响应
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String reply;
while((reply = br.readLine())!=null){
System.out.println("服务器:"+reply);
}
//释放资源
os.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.服务端程序
package cn.edu.xidian.see.*.Inet;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class LogicServicer {
public static void main(String[] args) {
try {
//接收客户端请求
//创建一个Socket
ServerSocket serverSocket = new ServerSocket(5000);
//使用accept()侦听并接收到此ServerSocket的连接
Socket socket = serverSocket.accept();//侦听到之前都是阻塞的
//获得输入流,获得用户的请求
//数据流输入
InputStream is = socket.getInputStream();
// BufferedReader读取字符流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info;
// BufferedReader的方法可以一行一行的读
while((info = br.readLine())!=null){
System.out.println("客户端:"+info);
}
//给客户端一个响应
String reply = "欢迎登陆";
//通过输出流将响应发送回给客户端
OutputStream os = socket.getOutputStream();
os.write(reply.getBytes());
//释放相应资源
os.close();
br.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.通过用户对象传参的方式
1.用户类程序
package cn.edu.xidian.see.*.Inet.user;
import java.io.Serializable;
public class User implements Serializable {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2.客户端程序
package cn.edu.xidian.see.*.Inet.user;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
public class LogicClient {
public static void main(String[] args) {
//创建一个客户端的Socket
try {
Socket socket = new Socket("localhost",5000);
//通过输出流发送请求
//将对象序列化变成输出流
User user = new User("樱桃小丸子","123456");
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
//关闭输出流,这是一个半关闭
socket.shutdownOutput();
//通过输入流接收服务器给我的响应
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String reply;
while((reply = br.readLine())!=null){
System.out.println("服务器:"+reply);
}
//释放资源
os.close();
oos.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.服务端程序
package cn.edu.xidian.see.*.Inet.user;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class LogicServicer {
public static void main(String[] args) {
try {
//接收客户端请求
//创建一个Socket
ServerSocket serverSocket = new ServerSocket(5000);
//使用accept()侦听并接收到此ServerSocket的连接
Socket socket = serverSocket.accept();//侦听到之前都是阻塞的
//获得输入流,获得用户的请求
//将对象反序列化变成输入流
InputStream is = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
User user = (User)ois.readObject();
System.out.println("客户端:"+user.getUsername()+"-"+user.getPassword());
//给客户端一个响应
String reply = "欢迎登陆";
//通过输出流将响应发送回给客户端
OutputStream os = socket.getOutputStream();
os.write(reply.getBytes());
//释放相应资源
ois.close();
os.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
提示:关于如何实现多客户请求,采用多线程的方式,然后写一个专门负责监听的应用主服务程序,以及一个专门负责处理请求的线程程序。
服务器与客户端的多线程连接(服务器与客户端可以无限对话)
1.服务器
1.一直监听客户请求。
2.一旦监听到有客户请求,立即创建一个线程,开启线程。
1.1 程序实现
package cn.edu.xidian.see.*.Inet.ThreadUser;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class LogicServicer {
//一直监听客户请求
//一旦监听到有客户请求,立即创建一个线程,开启线程
public static void main(String[] args) {
try {
//接收客户端请求
//创建一个Socket
ServerSocket serverSocket = new ServerSocket(8848);
System.out.println("启动服务器");
//使用accept()侦听并接收到此ServerSocket的连接
//一直监听客户请求
while(true){
Socket socket = serverSocket.accept();//侦听到之前都是阻塞的
System.out.println("已连接");
//创建一个和该客户端响应的线程
LogicThread logicThread = new LogicThread(socket);
logicThread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.线程
1.接收客户请求,基于客户的一个响应(这过程在之前是服务器要做的事情)。
2.线程构造方法中绑定客户端的Socket。
2.1 程序实现
package cn.edu.xidian.see.*.Inet.ThreadUser;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class LogicThread extends Thread{
//接收客户请求,基于客户一个响应(这在之前是服务器所需要做的事情)
//线程构造方法中去绑定客户端的Socket
private Socket socket;
public LogicThread(Socket socket) {
this.socket = socket;
}
//接收客户请求,基于客户的一个响应
public void run(){
InputStream is = null;
BufferedReader br = null;
PrintWriter pw = null;
OutputStream os = null;
try {
Scanner sc = new Scanner(System.in);
//获得输入流,获得用户的请求
while (true){
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
System.out.println(socket.getInetAddress() + "说" + br.readLine());
//给客户端一个响应,通过输出流将响应发送回客户端
String reply = sc.next();
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.println(reply);
pw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
pw.close();
os.close();
br.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.客户端
1.发送请求到服务器。
2.接收服务器的响应。
3.1 程序实现
package cn.edu.xidian.see.*.Inet.ThreadUser;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class LogicClient {
//发送请求到服务器
//接收服务器的响应
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
OutputStream os=null;
PrintWriter pw=null;
InputStream is=null;
BufferedReader br=null;
//创建一个客户端的Socket
try {
Socket socket = new Socket("localhost",8848);
System.out.println("客户端已启动");
//通过输出流发送请求
while(true){
String info =input.next();
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.println(info);
pw.flush();
//通过输入流接收服务器给我的响应
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
System.out.println("服务器:"+br.readLine());
}
//释放资源
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
os.close();
pw.close();
br.close();
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
使用代理服务器
1.代理的作用
1.出于安全原因防止远程主机了解到本地网络配置的秘密细节。
2.为了通过滤出站请求,限制可以浏览的网络。
3.出于性能考虑,运行多个用户从本地缓存获取同样的一些经常访问的文档,而不是重复从远程主机下载。