一、基于TCP协议的Socket编程
1、Socket(套接字)简介
Socket——套接字;
- 应用程序通过“套接字”向网络发出请求或者应答网络请求
- 最早是UNIX上的一套网络程序通讯的标准
- 已被广泛移植到其他平台
在Internet上的主机一般运行了多个服务软件,同时提供了几种服务,每种服务都打开一个Socket并绑定到一个端口上,不同的端口对应于不同的服务进程。
Socket实质上提供了进程通信的端点,网络上的两个程序通过一个双向的通讯链路实现数据的交换,这个双向链路的一端称为一个Socket。
Socket分为三种类型:流式套接字、数据报式套接字,原始式套接字。
原始式套接字(Sock_RAW):该接口允许对较低层次协议,如IP直接访问,可以接收本机网卡上的数据帧或数据包,对监听网络流量和分析很有用。
流式套接字(SOCK_STREAM):提供了一个面向连接,可靠的数据传输服务,数据无差错,无重复的发生,且按发送顺序接收,内设流量控制,避免超限。(TCP协议,类似打电话,有人呼叫,有人应答)
数据报式套节奏(SOCK_DGRAM):无连接服务,数据报以独立包形式被发送,不提供无差错保证,数据可能丢失或重复,并且接收顺序无需,对应的是UDP协议。
每一种套接字被封装道路不同的类中,这些类位于java.net包中。
Java平台封装的流式套接字类:Socket类和ServerSocket类。
2、Socket通信原理(*)
客户端发送用户信息给服务端,服务端响应后的结果在返回给客户端。
首先启动服务器,然后启动客户端;
传递字符串:
LoginServer.java
/**
* 服务器类
*/
public class LoginServer{
main(){
try{
//1.建立一个服务器Socket(ServerSocket)绑定指定端口并开始监听
ServerSocket serverSocket = new ServerSocket(8800);
//2.使用accept()方法阻塞等待监听,获得新的连接
Socket socket = serverSocket.accept();
//3.获得的输入流
InputStream is = socket.getInputStream();
//为了实现信息的高效读取,将字节流包装成字符流InputStreamReader,将字符流继续包装成BufferedReader,来缓冲读取字符
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//4.读取用户输入信息
String info = null;
while(!(info = br.readLine())!=null){
out.print("用户信息:"+info);
}
//5.关闭资源
br.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e){
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
}
}
}
LoginClient.java
/**
* 客户端类
*/
public class LoginClient{
main(){
try{
//1.建立客户端Socket连接,指定服务器的位置及端口
Socket socket = new Socket("localhost",8080);
//2.得到Socket的读写流
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
//输入流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//3.利用流按照一定的协议对Socket进行读/写操作
String info = "用户名:Zhangsan;密码:123456";
pw.write(info);
pw.flush();
socket.shudownOutput();
//接受服务器的响应并打印显示
String reply = null;
while(!(reply=br.readLine())==null){
out.print("客户端,服务器的响应为:"+reply);
}
//4.关闭资源
br.close();
is.close();
os.close();
socket.close();
}
}
}
传递对象:
User对象类,实现接口Serializable序列化。
import java.io.Serializable;
public class User implements Serializable{
}
LoginServer.java
/**
* 服务器类
*/
public class LoginServer{
main(){
try{
//1.建立一个服务器Socket(ServerSocket)绑定指定端口并开始监听
ServerSocket serverSocket = new ServerSocket(8800);
//2.使用accept()方法阻塞等待监听,获得新的连接
Socket socket = serverSocket.accept();
//3.获得的输入流
InputStream is = socket.getInputStream();
//获得流:可以对对象进行反序列化
ObjectInputStream ois = new ObjectInputStream(is);
//获得输出流:
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
//4.读取用户输入信息
User user = (User)ois.readObject();
out.print("用户信息:"+user.getLoginName);
//给客户一个响应
String reply = "Welcome!";
pw.write(reply);
pw.flush();
//5.关闭资源
pw.close();
os.close();
ois.close();
socket.close();
serverSocket.close();
} catch (IOException e){
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
}
}
}
LoginClient.java
/**
* 客户端类
*/
public class LoginClient{
main(){
try{
//1.建立客户端Socket连接,指定服务器的位置及端口
Socket socket = new Socket("localhost",8080);
//2.得到Socket的读写流
OutputStream os = socket.getOutputStream();
//对象序列化流
ObjectOutputStream oos = new ObjectOutputStream(os);
//输入流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//3.利用流按照一定的协议对Socket进行读/写操作
User user = new User();
user.setLoginName("Zhangsan");
oos.writeObject(user);
socket.shudownOutput();
//接受服务器的响应并打印显示
String reply = null;
while(!(reply=br.readLine())==null){
out.print("这是客户端,服务器的响应为:"+reply);
}
//4.关闭资源
br.close();
is.close();
oos.close();
os.close();
socket.close();
}
}
}
多用户同时访问服务器
一个客户访问启动一个线程,一个Socket对应一个线程。
线程类:ServerThread
/**
* 线程相关类
*/
public class ServerThread extends Thread{
//和本线程相关的Socket
Socket socket = null;
//通过创建线程的过程,可以实现一个新的线程和一个Socket绑定的过程
public serverThread(Socket socket){
this.socket = socket ;
}
//线程启动:响应用户请求
public void run(){
try{
//3.获得的输入流
InputStream is = socket.getInputStream();
//获得流:可以对对象进行反序列化
ObjectInputStream ois = new ObjectInputStream(is);
//获得输出流:
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
//4.读取用户输入信息
User user = (User)ois.readObject();
out.print("用户信息:"+user.getLoginName);
//给客户一个响应
String reply = "Welcome!";
pw.write(reply);
pw.flush();
//5.关闭资源
pw.close();
os.close();
ois.close();
socket.close();
serverSocket.close();
} catch (IOException e){
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
}
}
}
LoginServer.java
/**
* 服务器类
*/
public class LoginServer{
main(){
try{
//1.建立一个服务器Socket(ServerSocket)绑定指定端口并开始监听
ServerSocket serverSocket = new ServerSocket(8800);
//2.使用accept()方法阻塞等待监听,获得新的连接
Socket socket = null;
//声明一个变量,监听客户数量
int num = 0;
//一直处于监听状态
while(true){
socket = serverSocket.accept();
ServerThread serverThread = new ServerThread(socket);
//启动线程
serverThread.start();
num++;
out.print("客户数量"+num);
//客户的IP信息
InetAddress ia = socket.getInetAddress();
//客户的IP
String ip = ia.getHostAddress();
out.print("本客户的IP为:"+ip);
//客户的主机名称
String hostName = ia.getHostName();
out.print("本客户的主机为为:"+hostName);
}
} catch (IOException e){
e.printStackTrace();
}
}
}
3、Socket类以及ServerSocket类如何使用(*)
源码参照上部分;
4、InetAddress类如何使用(获取IP和hostName)
源码参照上部分;
二、基于UDP协议的Socket编程
1、DatagramSocket类如何使用
服务器端:AskServer
/**
* 服务器端
*/
public class AskServer{
main(){
try{
//1.创建接受方(服务器)套接字,并绑定端口号
DatagramSocket ds = new DatagramSocket(8800);
//2.确定数据包接受的数据的数组大小
Byte[] buf = new byte[1024];
//3.创建接受类型的数据包,数据将存储在数组中
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//4.通过套接字接受数据
ds.receive(dp);
//5.解析发送方法的数据
String mess = new String(buf,0,dp.getLength());
out.print("客户端:"+mess);
//响应客户端
String reply = "服务端响应客户端:!!!";
byte[] replys = reply.getBytes();
//响应地址
SocketAddress sa = dp.getSocketAddress();
//数据包
DatagramPacket dp2 = new DatagramPacket(replys,replys.length,sa);
//发送
ds.send(dp2);
//6.释放资源
ds.close();
}catch(SocketException e){
}catch(IOException e){
}
}
}
客户端:AskClient
/**
* 客户端
*/
public class AskClient{
main(){
//1.确定发送给服务器的信息,服务器地址以及端口
String mess = "一个问题!";
byte[] buf = mess.getBytes();
IntAddress ia = null;
try{
ia = InetAddress.getByName("localhost");
}catch(UnknownHostException e){
}
//端口
int port = 8800;
//2.创建数据包,发送指定长度的信息到指定服务器的指定端口
DatagramPacket dp = new DatagramPacket(buf, buf.length,ia,port);
//3.创建DatagramSocket对象
DatagramSocket ds = null;
try{
ds = new DatagramSocket();
}catch(){
}
//4.向服务器发送数据包
try{
ds.send(dp);
}catch(){
}
//接受服务器的响应并打印
byte[] buf2 = new byte[1024];
DatagramPacket dp2 = new DatagramPacket(buf2,buf2.length);
//通过套接字接受数据
try{
ds.receive(dp2);
}
//解析服务器的响应
String reply = new String(buf2,0,dp2.getLength());
out.print("服务器端的响应为:"+reply);
//5.释放资源
ds.close();
}
}
2、DatagramPacket类如何使用
源码参照上部分;
三、总结
3.1服务器编程步骤:
- 建立服务器Socket绑定指定端口开始监听
- 使用accept()方法阻塞等待监听,获得连接
- 建立输入和输出流
- 在已有的协议上产生回话
- 使用close()关闭流和Socket
3.2客户端编程步骤:
- 建立Socket连接,指定服务器位置及端口
- 得到Socket的读写流
- 利用流按一定的协议对Socket读/写操作
- 使用close()关闭流和Socket