网络通信基石Socket (上)
2016-04-14 12:42
721 查看
socket是什么,socket的原理是什么想必大家都了解个大概了,如果不懂的可以参考上一篇文章 socket原理的简单理解
这里我们也简单回顾一下:
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
1.Socket传输模式
1.1 Socket有两种主要操作方式
1.1.1 面向连接(TCP)
面向连接的Socket操作就像一部电话机,必须要等对方接上之后才能通话。所有的数据到达的顺序与它出发时的顺序是一样的。
面向连接的操作使用TCP协议,即此模式下必须先连接上目的地的Socket,连接上后Socket就可以使用一个流接口进行打开、读、写、关闭等操作。所有所发信息都会在另一端以同样的顺序被接收。安全性高,但效率低。
1.1.2 无连接的(UDP)
无连接的就像是一个邮件投递,没有保证,多个邮件到达时的顺序可能与出发时的顺序不一样。
无连接的操作使用数据报协议,一个数据报是一个独立的单元,它包含了这次投递的所有信息。可将其想象成一个信封,这个模式下的Socket不需要连接一个目的Socket,它只是简单的投出数据报。无连接的操作时快速和高效的,但是数据安全性不高。
2.Socket构造
android的编程语言是java,所以java提供的工具包在android都可以使用。
查看官方提供的API文档,在java.net包中提供两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务器端.
Socket类用来建立client的socket其构造方法如下:
ServerSocket类用来建立server的socket其构造方法如下:
其中的参数意义:
address :双向连接中另一方的IP地址
host:双向连接中另一方的主机名
port:双向连接中另一方的端口号
stream:指明Socket是流Socket还是数据报Socket
localPort:本地主机的端口号
localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址)
impl 是Socket的父类,即可以用来创建ServerSocket,又可以用来创建Socket
使用
再次强调:0~1023的端口号为系统所保留,选择端口号必须大于1024。
3.异常捕获
在创建Socket时如果发生错误,将产生IOException,所以在创建Socket和ServerSocket时必须捕获或抛出异常。
方法:
想了解try{}catch(){}和有关异常请看
android的异常处理——try、catch、finally、throw、throws
4.输入、输出流
java的数据传输是采用流的机制实现
流:实质是数据的有序排列,流可以看作是数据从某个源出来,输送到某个目的地
根据官方的API文档,java.net.Socket类提供了getInputStream()和getOutPutStream()来得到对应的输入(输出)流以进行读(写)操作,这两个方法分别返回InputStream和OutputStream类对象。
为了便于读(写)数据,可以在返回输入、输出流对象上建立过滤流。如:
字符流对象:
DataInputStream、DataOutPutStream、或PrintStream类对象。
文本方式流对象:
InputStreamReader和OutputStreamWriter、PrintWirter
处理代码如下:
放上一张java的io流图,这张图描述的已经很清楚,后面可能还会对java的io流资料进行整理,图是出网上摘来,如果涉及版权问题,第一时间处理
5.关闭Socket和流
在Socket使用完毕后需要将其关闭,以释放资源。
注意:在关闭Socket之前,应将与Socket相关的所有的输入、输出流先关闭,以释放资源。要注意关闭的顺序。
下例中 实现一个服务器和客户端通信。客户端发送数据并接受服务器发回的数据。效果图就不上了,放心测试可用。
客户端
简单描述一下:
应用布局只有三个:TextView , EditText , Button
在编辑框输入内容然后点击button就能收到服务器发送来的数据并显示在textView上
通过上例总结了一下:
使用Socket实现客户端的步骤;
1、通过IP地址和端口实例化Socket,请求连接服务器
2、获取Socket上的流以进行读写
3、把流包装进OutputStream/InputStream的实例
4、对Socket进行读写
5、关闭打开的流
创建服务器的步骤:
1、指定端口实例化一个ServerSocket
2、调用ServerSocket的accept()以在等待连接期间造成阻塞
3、获取位于该层Socket的流以进行读写操作
4、将数据封装成流
5、对Socket进行读写
6、关闭打开的流
上面这个例子只是能实现单个客户点的发送和接收数据,下一章将对实现多个客户端的通信。
这里我们也简单回顾一下:
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
1.Socket传输模式
1.1 Socket有两种主要操作方式
1.1.1 面向连接(TCP)
面向连接的Socket操作就像一部电话机,必须要等对方接上之后才能通话。所有的数据到达的顺序与它出发时的顺序是一样的。
面向连接的操作使用TCP协议,即此模式下必须先连接上目的地的Socket,连接上后Socket就可以使用一个流接口进行打开、读、写、关闭等操作。所有所发信息都会在另一端以同样的顺序被接收。安全性高,但效率低。
1.1.2 无连接的(UDP)
无连接的就像是一个邮件投递,没有保证,多个邮件到达时的顺序可能与出发时的顺序不一样。
无连接的操作使用数据报协议,一个数据报是一个独立的单元,它包含了这次投递的所有信息。可将其想象成一个信封,这个模式下的Socket不需要连接一个目的Socket,它只是简单的投出数据报。无连接的操作时快速和高效的,但是数据安全性不高。
2.Socket构造
android的编程语言是java,所以java提供的工具包在android都可以使用。
查看官方提供的API文档,在java.net包中提供两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务器端.
Socket类用来建立client的socket其构造方法如下:
Socket(InetAddress address,int port); Socket(InetAddress address,int port,boolean stream); Socket(String host,int port); Socket(String host,int port,boolean stream); Socket(SocketImpl impl); Socket(String host,int port,InetAddress localAddr,int localPort); Socket(InetAddress address,int port,InetAddress localAddr,int localPort);
ServerSocket类用来建立server的socket其构造方法如下:
ServerSocket(int port); ServerSocket(int port,int backlog); ServerSocket(int port,int backlog,InetAddress bindAddr);
其中的参数意义:
address :双向连接中另一方的IP地址
host:双向连接中另一方的主机名
port:双向连接中另一方的端口号
stream:指明Socket是流Socket还是数据报Socket
localPort:本地主机的端口号
localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址)
impl 是Socket的父类,即可以用来创建ServerSocket,又可以用来创建Socket
使用
//client端 Socket client = new Socket("192.168.8.254",9999); //server端 ServerSocket server = new ServerSocket(9999);
再次强调:0~1023的端口号为系统所保留,选择端口号必须大于1024。
3.异常捕获
在创建Socket时如果发生错误,将产生IOException,所以在创建Socket和ServerSocket时必须捕获或抛出异常。
方法:
try{ //这里执行可能出现异常的程序 }catch(IOException e){//catch()中的参数视程序出现的异常而定 //这里可以做相应的异常处理 }
想了解try{}catch(){}和有关异常请看
android的异常处理——try、catch、finally、throw、throws
4.输入、输出流
java的数据传输是采用流的机制实现
流:实质是数据的有序排列,流可以看作是数据从某个源出来,输送到某个目的地
根据官方的API文档,java.net.Socket类提供了getInputStream()和getOutPutStream()来得到对应的输入(输出)流以进行读(写)操作,这两个方法分别返回InputStream和OutputStream类对象。
为了便于读(写)数据,可以在返回输入、输出流对象上建立过滤流。如:
字符流对象:
DataInputStream、DataOutPutStream、或PrintStream类对象。
文本方式流对象:
InputStreamReader和OutputStreamWriter、PrintWirter
处理代码如下:
//字符流方式 PrintStream os = new PrintStream(new BufferedOutputStream(Socket.getOutputStream())); DataInputStream is = new DataInputStream(socket.getInputStream()); //文本方式 PrintWriter out = new PrintWriter(socket.getOutStream(),true); BufferedReader in = new ButfferedReader(new InputStreamReader(Socket.getInputStream()));
放上一张java的io流图,这张图描述的已经很清楚,后面可能还会对java的io流资料进行整理,图是出网上摘来,如果涉及版权问题,第一时间处理
5.关闭Socket和流
在Socket使用完毕后需要将其关闭,以释放资源。
注意:在关闭Socket之前,应将与Socket相关的所有的输入、输出流先关闭,以释放资源。要注意关闭的顺序。
os.close();//输出流先关闭 is.close();//输入流其次 socket.close();//最后关闭Socket
下例中 实现一个服务器和客户端通信。客户端发送数据并接受服务器发回的数据。效果图就不上了,放心测试可用。
import java.io.BufferedWriter; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Prin 4000 tWriter; import java.net.ServerSocket; import java.net.Socket; public class server implements Runnable{//服务器实现 注意:该程序需要单独编译,并在命令行模式下启动 String str=""; public void run(){ try{ ServerSocket serverSocket = new ServerSocket(8899);//创建ServerSocket 设置端口号为9999 while (true){ Socket client = serverSocket.accept();//通过accept监听接受客户端请求 System.out.println("accept"); try{ PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(client.getOutputStream())),true); //通过PrintWriter向服务器发送消息,但需要通过Socket对象来取得其输出流 out.println("server message 让我放假"); //BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));//通过BufferedReader对象接收客户端消息 InputStream in = client.getInputStream();//定义输入流,来自于socket的输入流 byte[] bytes2 = new byte[20]; in.read(bytes2);//读取输入流数据 String str = new String(bytes2);//转换成字符串 System.out.println(str); out.flush(); out.close();//关闭流 in.close(); }catch (Exception e){ System.out.println(e.getMessage()); e.printStackTrace(); }finally{ client.close();//关闭 System.out.println("close"); } } }catch (Exception e){ System.out.println(e.getMessage()); } //System.out.println(str); } public static void main(String a[]){//main函数用来开启服务器 Thread desktopServerThread = new Thread(new server()); desktopServerThread.start();//开启线程 } }
客户端
简单描述一下:
应用布局只有三个:TextView , EditText , Button
在编辑框输入内容然后点击button就能收到服务器发送来的数据并显示在textView上
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private EditText edt; private Button but; private TextView tv; private Handler handler=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); edt = (EditText)findViewById(R.id.editText1); but = (Button)findViewById(R.id.button1); tv = (TextView)findViewById(R.id.textView1); handler = new Handler(){ public void handleMessage(Message msg){ switch(msg.what){ case 1 : tv.setText(msg.obj.toString()); break; case 2 : Toast.makeText(MainActivity.this,"Connection successful!", Toast.LENGTH_LONG).show(); break; } } }; but.setOnClickListener(new OnClickListener(){ public void onClick(View v){ new Thread(new Runnable(){ public void run(){ try { @SuppressWarnings("resource") Socket socket = new Socket("192.168.8.107",8899); //要判断当前的Socket对象是否处于连接状态,必须同时使用isClose和isConnected方法, //即只有当isClose返回false,isConnected返回true的时候Socket对象才处于连接状态。 if (socket.isClosed() == false && socket.isConnected() == true){ //还有一个问题就是&&的用法!! /*******注意不能在主线程里里更新UI************/ // Toast.makeText(MainActivity.this,"Connection successful!", // Toast.LENGTH_LONG).show();// Message message = new Message(); message.what=2; handler.sendMessage(message); } InputStream in = socket.getInputStream(); OutputStream output = socket.getOutputStream(); String str = edt.getText().toString(); output.write(str.getBytes()); byte[] buffer = new byte[in.available()]; in.read(buffer); String msg = new String(buffer,"GB2312");//乱码需要加GB2312 Message message = new Message(); message.obj=msg; message.what=1; handler.sendMessage(message); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
通过上例总结了一下:
使用Socket实现客户端的步骤;
1、通过IP地址和端口实例化Socket,请求连接服务器
2、获取Socket上的流以进行读写
3、把流包装进OutputStream/InputStream的实例
4、对Socket进行读写
5、关闭打开的流
创建服务器的步骤:
1、指定端口实例化一个ServerSocket
2、调用ServerSocket的accept()以在等待连接期间造成阻塞
3、获取位于该层Socket的流以进行读写操作
4、将数据封装成流
5、对Socket进行读写
6、关闭打开的流
上面这个例子只是能实现单个客户点的发送和接收数据,下一章将对实现多个客户端的通信。