java套接字Socket、ServerSocket
/**
* 测试ServerSocket类和Socket类
* Socket为传输层供给应用层的编程接口,用于应用层与传输层之间的数据通信
* Socket类使用TCP协议,客户端向服务端发出连接请求,经过三次握手后客户端和服务端不再有区别,都可收发数据
* ServerSocket为服务端,“请求-响应”模式需要先运行服务端监听端口,再通过客户端进行访问
*/
public class TestServerSocket extends Thread{
public void server(){
//创建服务器线程
ServerSocket ss=null;
Socket client=null;
BufferedReader br = null;
BufferedOutputStream bos = null;
try {
ss = new ServerSocket(8888);
//创建服务端,构造器传参端口号port,设定为8888端口,这时服务端尚未运行
client = ss.accept();
//.accept()方法监听端口,线程进入阻塞状态,直到accept接收到连接,返回Socket对象,这个对象象征客户端,通过这个对象来与客户端收发数据
br = new BufferedReader(new InputStreamReader(client.getInputStream()));
//.getInputStream()返回输入流,数据源为客户端通过输出流发送的内容,如果是http请求,发送的内容包括请求首行、请求头、空行、请求体
for (String text = br.readLine();text!=null;text = br.readLine()){
System.out.println(text);
//使用缓冲字符流输出整行内容,当读完客户端当前发送的所有内容后.read()会继续阻塞等待接收新的内容,这使得.readLine()并不会返回null。只有当客户端关闭输出流时.readLine()才会返回null表示读完了
}
bos = new BufferedOutputStream(client.getOutputStream());
//.getOutputStream()返回字节输出流,用于向客户端发数据,在建立连接之后双方都可以收和发
bos.write("HTTP/1.1 200 OK\r\n\r\nHello World\r\n".getBytes());
//字节缓冲流发送字节数组
bos.flush();
//数据写完必须要flush()
client.shutdownOutput();
//发送完数据关闭输出流,以解除接收方.read()造成的阻塞
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (client != null) {
try {
client.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public void client(){
//创建客户端线程
Socket socket=null;
PrintWriter pw =null;
BufferedInputStream bis = null;
try {
socket = new Socket("127.0.0.1",8888);
//创建Socket对象,需要指定域名/ip地址和端口号,构造器返回的对象象征服务端,通过这个对象即可与服务端收发数据
pw = new PrintWriter(socket.getOutputStream(),true);
//.getOutputStream()返回字节输出流,通过这个字节输出流可以向服务端发送数据
//PrintWriter作为处理流包裹字节流时设定boolean autoFlush=true会套缓冲流并且自动flush
//PrintWriter构造器中不设定autoFlush时默认false,且autoFlush只有在println自动换行中生效,使用print()不会flush
pw.println("GET / HTTP/1.1\r\n\r\n");
socket.shutdownOutput();
//发送完数据需要关闭输出流 ,这样对方才能read()=-1,一旦关闭输出流无法再打开
bis = new BufferedInputStream(socket.getInputStream());
//接收服务器返回的数据
int b;
while ((b=bis.read())!=-1){
//也可以规定发送一段符号代表数据结尾,或者数据开头标明字节数,读到一定数量结束循环
System.out.write(b);
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if (pw != null) {
pw.close();
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
boolean isServer;
//构造器区分客户端和服务端
public TestServerSocket(String name, boolean isServer) {
super(name);
this.isServer = isServer;
}
@Override
public void run() {
if (isServer){
server();
}else {
client();
}
}
public static void main(String[] args) {
new TestServerSocket("服务端",true).start();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new TestServerSocket("客户端",false).start();
//结果:服务端线程打印GET / HTTP/1.1
//客户端打印HTTP/1.1 200 OK Hello World
}
}