文章目录
socket 的简单介绍
- socket 属于网络分层中的应用层
- socket 可以使用 TCP/IP 协议进行可靠传输,也可以使用 UDP 协议进行不可靠传输
- If a UDP socket is used, TCP/IP related socket options will not apply.(如果使用 UDP,则TCP/IP 的功能将失效)
- Use DatagramSocket instead for UDP transport.(使用DatagramSocket代替UDP传输。)
- socket 是全双工通讯传输,即服务器端和客户端发送或者接受数据可以同步进行
什么是socket?
Socket 并不同于 http协议,而是一个API
Socket接口是TCP/IP网络的API
,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP 网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。
提出两个问题
socket 中的等待
在 socket 的使用中不得不注意这种问题,服务端收到客户端的请求,开始接受数据,如果客户端没有给出数据结束的信号,那么理论上服务器端是不应该停止等待的,这样在单线程开发中,服务器端就会阻塞了。
假如我们期望客户端传递给服务器端之后也能得到服务器端的响应,这个问题就不得不解决。
readLine的阻塞
在上一篇文章(BufferedReader.readLine)中我们提到了这个问题,readLine 本身是一个阻塞的方法,除非遇到了换行符,再加之服务器端一般会使用死循环来调用这个方法,所以当客户端没有给出传输结束的信号时,这个环节也会造成阻塞。
假如我们期望客户端传递给服务器端之后也能得到服务器端的响应,这个问题就不得不解决。
解决方案之 socket 和 IO 的技巧
上面的问题其实可以分为两类,一类是客户端只会和服务器端交互一次,即一次发送一次接收;一类是客户端和服务器端存在多次穿插交互,即多次穿插的发送和接收
所以关键在于,客户端给服务器端以信号,让其感知到一次发送结束,然后根据客户端是否要再次发送数据来决定客户端采取的具体措施
-
技巧点如下:
1、 如果客户端和服务端只有一次交互(相互只发送一次信息),可以调用客户端的 socket.shutdownOutput(); 方法,这样服务器端就可以知道客户端发送结束了不会再阻塞。
但是注意不能用任何流的 close() 方法,那样 客户端socket 会关闭,就无法接受服务器端返回的信息了。
2、对于如果是多交互可以在readline死循环里加一个判断,
按行读到某一个特殊字符就可以中断循环,这样可以循环n次for(;(str=bufferedReader.readLine())!=null;){ if("end".equals(str)){ break; } System.out.print(str); }
3、发送数据使用 PrintWriter.println(“内容”),这个方法内含 flush() 和 换行,可以立即发送缓冲区数据,并且防止服务器端使用 readLine() 方法时阻塞。注意 PrintWriter 需要调用构造方法 。PrintWriter printWriter=new PrintWriter(writer,true);否则需要手动调用 flush()发送缓存区的数据。
4、在一个解决思路是使用多线程,既然读会阻塞,那么就另起一个线程进行写,socket 是全双工通讯传输的。
5、一般而言,服务器端要使用线程池。
socket 服务端和客户端一次交互的例子
实现客户端访问服务端
客户端向服务端发送数据
服务端接受完数据后返回客户端数据
结束
服务端代码
import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
/**
* socket 简单例子 服务端
* @Author: jie.wu
*/
public class Server {
public static void main(String [] args){
Server s=new Server();
s.start();
System.out.println("系统默认编码格式="+Charset.defaultCharset().name());
}
public void start(){
//服务端声明
ServerSocket serverSocket;
//客户端请求声明
Socket clientSocket;
//客户端输入流声明
InputStream inputStream=null;
BufferedReader bufferedReader =null;
//客户端输出流声明
OutputStream outputStream=null;
PrintWriter printWriter=null;
try{
//初始化服务端
serverSocket=new ServerSocket(10000);
//初始化客户端
clientSocket=serverSocket.accept();
//初始化客户端输入流
inputStream=clientSocket.getInputStream();
//读取客户端传输的内容
//BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset().name()));
//输出
String str="";
System.out.print("客户端输入:");
//客户端没有发送结束服务端会阻塞,使用 readline 方法没有读到换行符会阻塞
for(;(str=bufferedReader.readLine())!=null;){
//和客户端约定输出结束的符号
if("end".equals(str)){
break;
}
System.out.print(str);
}
System.out.println();
/*
char b[]=new char[8192];
StringBuffer sbf=new StringBuffer();
for(int n=0;(n=bufferedReader.read(b,0,n))!=-1;){
sbf.append(b);
}
System.out.println(sbf.toString());
*/
//初始化客户端输出流
outputStream=clientSocket.getOutputStream();
printWriter=new PrintWriter(new OutputStreamWriter(outputStream, Charset.defaultCharset().name()),true);
printWriter.println("你好,我是服务器端");
printWriter.println("end");
}catch(Exception e){
System.out.println("服务器报错"+e);
}finally {
try{
if(inputStream!=null){inputStream.close();}
if(bufferedReader!=null){bufferedReader.close();}
if(outputStream!=null){ outputStream.close();}
if(printWriter!=null){ printWriter.close();}
}catch(Exception e){
System.out.println("服务器真的报错了"+e);
}
}
}
}
客户端代码
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.Charset;
/**
* @Author: jie.wu
* @Version: v1.0
* @Date:2019-01-29 14:09
*/
public class Client {
public static void main(String [] args){
Client c=new Client();
c.client();
}
public void client(){
Socket socket=new Socket();
OutputStream outputStream=null;
PrintWriter printWriter=null;
InputStream inputStream=null;
BufferedReader bufferedReader=null;
try {
socket.connect(new InetSocketAddress("127.0.0.1", 10000));
outputStream=socket.getOutputStream();
printWriter=new PrintWriter(new OutputStreamWriter(outputStream, Charset.defaultCharset().name()),true);
printWriter.println("你好,我是客户端");
printWriter.println("end");
inputStream=socket.getInputStream();
bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
//readLine 会造成服务器端阻塞,服务器端要么加以判断主动关闭,要么等待客户端关闭,要么新起一个线程
//客户端可以主动结束发送信息
//socket.shutdownOutput();
String str;
System.out.print("服务端回复:");
for(;(str=bufferedReader.readLine())!=null;){
if("end".equals(str)){
break;
}
System.out.print(str);
}
/*
char[] b=new char[8192];
StringBuffer sbf=new StringBuffer();
for(int n=0;(n=bufferedReader.read(b,0,n))>-1;){
sbf.append(b);
}
System.out.println(sbf.toString());
*/
}catch(Exception e){
System.out.println("报错了"+e);
}finally {
try{
if(outputStream!=null){outputStream.close();}
if(printWriter!=null){printWriter.close();}
if(inputStream!=null){inputStream.close();}
if(bufferedReader!=null){bufferedReader.close();}
}catch(Exception e){
System.out.println("真的报错了"+e);
}
}
}
}