Java最初是作为网络编程语言出现的,它对网络的高度支持,使得客户端和服务器流畅的沟通变成现实。而在网络编程中,使用最多的就是Socket,每一个实用的网络程序都少不了它的参与。但Socket的底层机制却相当棘手,幸运的是,Java平台给我们一些虽然简单但是相当强大的类,可以使你更简单有效的使用Socket开发而无需了解底层机制。
请思考一下网络编程要解决的问题:而下面这些问题都可以用Socket来解决
- 如何建立两个节点(电脑)之间的网络连接?
- 如何向另外一个节点(电脑)发送信息?
- 如何从外部节点(电脑)接收一个请求并给予响应?
- 如何利用网络协议(TCP、UPD)?
已被广泛移植到了其他平台。
Socket实质上提供了进程通信的端点,网络上的两个程序通过一个双向的通讯链路实现数据的交换,这个双向链路的一端称为一个Socket。
而Socket的分类:
原始式套接字;该接口允许对较低层次协议,如IP直接访问。可以接收本机网卡上的数据帧或数据包,对监听网络流量和分析很有用。
流式套接字:提供了一个面向连接,可靠的数据传输服务,数据无差错,无重担的发送,并且按发送顺序接收。其实它对应使用的是TCP协议,(他的理论就像两个人打电话,他们必须建议一个连接,若电话线等,有人呼叫,有人应答,所有顺序是它们出发时候的顺序与它们到达时的顺序一样。电话这头说什么,另外一头就原原本本的听到什么)
数据报式套接字:它是无连接服务。数据报以独立包形式被发送,不提供无差错保证,数据可能丢失或复制,并且接收顺序无序。其实它对应使用的是UDP协议
Socket通信原理:
Socket非常类似于电话插座:
1,电话的通信双方相当于相互通信的两个进程;
2,任何用户在通话之前,首先要占有一部电话机,相当于申请一个Socket——>同时,要知道对方的号码,相当于对方也有一个固定的Socket;
3,然后向对方拨号呼叫,相当于发送连接请求——>对方假如在场并空闲,相当于可以接收连接请求
4,拿起电话两人就可以通话了,相当于连接成功;
5,双方通话的过程,是一方向电话机发出信号和对方从另一个电话机接收信息的过程,相当于,向Socket发送数据和从Socket接收数据;
6,通话结束后,一方挂起电话相当于关闭Socket,撤销连接。
Socket的底层比较棘手,但Java封装并提供了相应的类,基于java.net包中,如Socket类,ServerSocket类。。。。
Socket类常用方向如下:
ServerSocket类常用方法如下:
O了,讲了这么多理论原理的东西,我来讲讲编码方面的知识吧,上述理论只是过一个大概,并不详细,若对理论感兴趣的,可以上网上查询相关资料:
我写了一个简单的例子:一个Socket服务器,一个是调用此服务器端口的客户端。以下例子就是客户端向服务器的请求。代码如下:
package com.qhs.sockettest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Socket服务器相关代码
* 编码完成后,要先运行服务器开启端口,然后再运行客户端。
*/
public class LoginServer {
public static void main(String args []){
try {
//1.建立一个服务器Socket绑定一个端口并开始监听
ServerSocket serverSocket = new ServerSocket(8800);
//2.使用accept()方法阻塞等待监听,获得新的连接
Socket socket = serverSocket.accept();
//3.获得输入流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//4.获得用户输入信息
String info = null;
while(!((info=br.readLine())==null)){
System.out.println("我是服务器,用户信息为:" + info);
}
//5.关闭资源
br.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客户端代码如下:
package com.qhs.sockettest;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 客户端
*/
public class LoginClient {
public static void main(String args []){
try {
//1.建立客服端Socket连接,指定服务器的位置以及端口
Socket socket = new Socket("localhost", 8800);
//2.得到Socket读写流
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
//3.利用流按照一定协议对Socket进行读/写操作
String info = "用户名:Jeff;用户密码:123456";
pw.write(info);
pw.flush();
//4.关闭资源
pw.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试进行时,请先运行服务器的代码,使得端口打开。然后再运行客户端去连接。最后在控制台输入的结果如图:
上面的例子就比如一个人打电话,另外一个人只是听,不说话,单向通信。所以运行后,后台只有客户端有服务器的信息
接下来,我们在上述代码中添加一些代码,使得服务器也有响应,等于双方相互通信的目的:
服务器代码:
package com.qhs.sockettest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Socket服务器相关代码
* 编码完成后,要先运行服务器开启端口,然后再运行客户端。
*/
public class LoginServer {
public static void main(String args []){
try {
//1.建立一个服务器Socket绑定一个端口并开始监听
ServerSocket serverSocket = new ServerSocket(8800);
//2.使用accept()方法阻塞等待监听,获得新的连接
Socket socket = serverSocket.accept();
//3.获得输入流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//服务器的响应加进来的代码:获得输出流
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
//4.获得用户输入信息
String info = null;
while(!((info=br.readLine())==null)){
System.out.println("我是服务器,用户信息为:" + info);
}
//添加代码:给客户一个响应
String reply = "服务的响应>>>>Welcome!";
pw.write(reply);
pw.flush();
//5.关闭资源
pw.close();
os.close();
br.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客户端代码:
package com.qhs.sockettest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 客户端
*/
public class LoginClient {
public static void main(String args []){
try {
//1.建立客服端Socket连接,指定服务器的位置以及端口
Socket socket = new Socket("localhost", 8800);
//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 = "用户名:Jeff;用户密码:123456";
pw.write(info);
pw.flush();
socket.shutdownOutput(); //关闭socket
//添加代码:接收服务器响应并打印显示
String reply = null;
while(!((reply=br.readLine())==null)){
System.out.println("我是客户端,接收到服务器的响应为:" + reply);
}
//4.关闭资源
br.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
我们运行测试一下,先运行服务器,再运行客户端。我们会看到,这次后台不只是客户端接收到了服务器给的请求信息,在服务器后台,也输出了相应的响应信息:我们可以点击下图所示的切换来查看客户端与服务器接收与响应的后台信息:
最后,我们来总结一下Socket中服务器与客户端的各步骤。
服务器端:
- 建立一个服务器Socket绑定指定的端口并开始监听;
- 使用accept()方法阻塞等待监听,获取新的连接;
- 建立输入和输出流;
- 在已有的协议上产生会话;
- 使用close()方法关闭流和Socket;
- 建立客户端Socket连接,指定服务器的位置以及端口;
- 得到Socket的读写流;
- 利用流按照一定的协议对Socket进行读/写操作;
- 使得close()方法关闭流和Socket
这是Socket网络编程之传递字符类型,接下来我会在我博客另外一篇中Socket升级传递对象编程