Android TCP/UDP通信试炼以及部分程序

TCP(传输控制协议)和UDP(用户数据报协议是网络体系结构TCP / IP模型中传输层一层中的两个不同的通信协议。

TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工字节流,TCP套接口是字节流套接口(stream socket)的一种。
UDP:用户数据报协议.UDP是一种无连接协议.UDP套接口是数据报套接口(datagram socket)的一种。

1)基本TCP客户 - 服务器程序设计基本框架

说明:(三路握手)
        1.客户端发送一个SYN段(同步序号)指明客户打算连接的服务器端口,以及
        初始序列号(ISN)。2.服务器发回包含服务器的初始序号的SYN报文段作为应答。同时,将确认序号(ACK)设置为客户的ISN加1以对客户的SYN报文段进行确认。一个SYN将占用一个序号
        。3.客户必须确认序号设置为服务器的ISN加1对服务器的SYN报文段进行确认。

2)基本UDP客户 - 服务器程序设计基本框架流程图

 
3)UDP和TCP的对比:
从上面的流程图比较我们可以很明显的看出UDP没有三次握手过程。
简单点说.UDP处理的细节比TCP少.UDP不能保证消息被传送到(它也报告消息没有传送到)目的地.UDP也不保证数据包的传送顺序.UDP把数据发出去后只能希望它能够抵达目的地。
TCP优缺点:
优点:
        。1.TCP提供以认可的方式显式地创建和终止连接
        。2.TCP保证可靠的,顺序的(数据包以发送的顺序接收)以及不会重复的数据传输
        3.TCP处理流控制
        。4 . 允许数据优先
        5.如果数据没有传送到,则TCP套接口返回一个出错状态条件
        。6.TCP通过保持连续并将数据块分成更小的分片来处理大数据块.-无需程序员知道
缺点:TCP在转移数据时必须创建(并保持)一个连接。这个连接给通信进程增加了开销,让它比UDP速度要慢。
UDP优先点:
        1.UDP不要求保持一个连接
        2.UDP没有因接收方认可收到数据包(或者当数据包没有正确抵达而自动重传)而带来的开销。3.
        设计UDP的目的的是用于短应用和控制消息
        4.在一个数据包连接一个数据包的基础上,UDP要求的网络带宽比TDP更小。

插座

通常我们翻译为套接字,是应用层和传输层之间的一个抽象层,网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个插座,一个插座由一个IP 地址和一个端口号唯一确定。

ServerSocket的

ServerSocket的用于监听来自客户端的插座连接,如果没有连接将会一直等待下去,主要用于构建服务器端。下面是一些的ServerSocket的一些常用方法

ServerSocket(int port):用指定的一个端口来创建一个ServerSocket,端口的值在0-65535

ServerSocket(int port,int backlog):增加一个改变连接队列长度的参数backlog

ServerSocket(int port,int backlog,InetAddress localAddress):使用localAddress参数来将ServerSocket绑定到指定的IP

 接受():如果接收到连接请求,这个方法返回一个与连接的客户端套接字对应的插槽;否则该方法处于一直等待,阻塞线程。

关闭():在ServerSocket的使用完毕后调用该方法来关闭


插座

客户端通常可以使用插座的构造器来连接到指定的服务器

Socket(InetAddress / String remoteAddress,int port):创建连接到指定远程主机,远程端口的Socket,并且没有指定本地地址,本地端口,默认使用本地主机的默认IP地址,默认使用系统动态分配的端口。

Socket(InetAddress / String remoteAddress,int port,InetAddress localAddr,int localPort):创建指定远程主机,远程端口的Socket,并指定本地IP地址和端口,适用于本地主机有多个IP的情况


的getInputStream():返回该插座对象对应的输入流,让程序通过该输入流从插座里面获取数据

的getOutputStream():返回该插座对象对应的输出流,让程序通过该输出流向插座写入数据

一个简单的实例

服务端

[java]   查看纯 文本  
  1. public static void  main(String [] args)  throws  IOException {    
  2.         //创建一个ServerSocket的用于监听客户端的请求  
  3.         ServerSocket serverSocket =  new  ServerSocket(80 80);  
  4.         while  (true ){  
  5.             //当接收到客户端的请求时,产生一个对应的插座  
  6.             套接字socket = serverSocket.accept();  
  7.             OutputStream os = socket.getOutputStream();  
  8.             os.write("jkghjgjhg \n".getBytes("utf-8"));  
  9.             os.close();  
  10.             socket.close();  
  11.         }  
  12.     }  
服务端的程序运行在PC上面,定义一个Java类直接运行就可以了。上面代码是创建了一个ServerSocket在8080的端口监听,一个服务端不只对应一个客户端,需要不断的接收来自所以客户端的请求,收到一个请求就产生一个Socket。ServerSocket调用accept方法来接收客户端的请求,得到一个Socket对象,向这个Socket里面写入数据,然后分别关闭流,关闭Socket。

客户端

[java]  view plain  copy
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     private static final String TAG = "lzy";  
  4.     private Context context;  
  5.     private TextView mTextView;  
  6.   
  7.   
  8.     @Override  
  9.     protected void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.activity_main);  
  12.         context = this;  
  13.         mTextView = (TextView) findViewById(R.id.tv);  
  14.   
  15.     }  
  16.   
  17.     public void bt(View view) {  
  18.         new Thread() {  
  19.             @Override  
  20.             public void run() {  
  21.                 try {  
  22.                     Socket socket = new Socket("172.16.7.234"3000);  
  23.                     BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"utf-8"));  
  24.                     String line = br.readLine();  
  25.                     Log.i(TAG, "读取数据:" + line);  
  26.                     br.close();  
  27.                     socket.close();  
  28.   
  29.                 } catch (IOException e) {  
  30.                     e.printStackTrace();  
  31.                 }  
  32.             }  
  33.         }.start();  
  34.   
  35.     }  
  36. }  
首先是建立远程连接的Socket,然后通过这个Socket获取到数据。这里的端口号和上面的要对应

设置Socket的超时时间

setSoTimeout(int timeout) :设置Socket的超时时间,不能小于0,如果使用Socket进行读写操作完成之前已经超出了这个时间,那么就会抛出SocketException。


设置Socket连接服务器超时时间

[java]  view plain  copy
  1. //创建一个无连接的Socket  
  2. Socket socket = new Socket();  
  3. //让Socket连接到远程服务器,如果经过1秒没有连接到则认为超时  
  4. socket.connect(new InetSocketAddress(host,port),1000);  
在Socket的构造参数里面并没有设置超时时长这个参数,所以需要先创建一个无连接的Socket,调用它的connect方法连接远程服务器,而这里就可以设置一个超时时长的参数。


再写一个聊天室的小应用来看看Socket的使用


服务端

在Android Studio创建一个服务端,点击File->New->New Module,创建一个Java Library

[java]  view plain  copy
  1. public class MyServer {  
  2.     public static List listSocket = new ArrayList<>();  
  3.   
  4.     public static void main(String[] args) throws IOException {  
  5.         ServerSocket serverSocket = new ServerSocket(808);  
  6.         while (true) {  
  7.             //等待连接  
  8.             Socket socket = serverSocket.accept();  
  9.             System.out.println("a target online");  
  10.             listSocket.add(socket);  
  11.             //每当客户端连接成功 后启动一条线程为该客户端服务  
  12.             new Thread(new ServerThread(socket)).start();  
  13.         }  
  14.     }  
  15. }  

listSocket是用于保存客户端连接后产生的Socket对象。

这段代码首先是在8080端口创建一个ServerSocket,然后是一个while的死循环调用ServerSocket的accept方法不停的接收来自客户端的连接请求,然后为这个客户端开启一个线程为其服务

  1. public class ServerThread implements Runnable {    
  2.     private Socket socket;  
  3.     private BufferedReader br;  
  4.   
  5.     public ServerThread(Socket socket) throws IOException {  
  6.         this.socket = socket;  
  7.         br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));  
  8.     }  
  9.     @Override  
  10.     public void run() {  
  11.         String content;  
  12.         //不断把客服端的数据读取出来  
  13.         while ((content = readFromClient()) != null) {    
  14.             //把收到的消息遍历发给每一个 连接了的客户端  
  15.             for (Iterator iterator = MyServer.listSocket.iterator(); iterator.hasNext(); ) {  
  16.                 Socket socket = iterator.next();  
  17.                 //打印出客服端的Ip和端口号  
  18.                 System.out.println("IP:" + socket.getInetAddress() + "    port:" + socket.getPort());  
  19.                 try {  
  20.                     OutputStream os = socket.getOutputStream();  
  21.                     os.write((content + "\n").getBytes("utf-8"));  
  22.                 } catch (IOException e) {  
  23.                     e.printStackTrace();  
  24.                 }  
  25.             }  
  26.         }  
  27.     }   
  28.     private String readFromClient() {  
  29.         try {  
  30.             return br.readLine();  
  31.         } catch (IOException e) {  
  32.             e.printStackTrace();  
  33.             MyServer.listSocket.remove(socket);  
  34.         }  
  35.         return null;  
  36.     }  
  37. }  

在这个线程中把Socket的数据读到BufferedReader里面,然后通过一个while循环把消息读取来遍历发送给每一个连接了的客户端。

readFromClient用来读取接收到的客户端数据,如果发生了异常就把这个Socket移除。

客户端

MainActivity

[java]  view plain  copy
  1. public class MainActivity extends AppCompatActivity {  
  2.     private static final String TAG = "lzy";  
  3.     private List list = new ArrayList<>();  
  4.     private RecyclerView mRecyclerView;  
  5.     private Context context;  
  6.     private ChatAdapter adapter;  
  7.     private EditText mEditText;  
  8.     private ClientThread clientThread;  
  9.     //用于发送接收到的服务端的消息,显示在界面上  
  10.     private Handler handler = new Handler() {  
  11.         @Override  
  12.         public void handleMessage(Message msg) {  
  13.             list.add(msg.obj.toString());  
  14.             adapter.notifyDataSetChanged();  
  15.         }  
  16.     };  
  17.     @Override  
  18.     protected void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.activity_main);  
  21.         context = this;  
  22.         mRecyclerView = (RecyclerView) findViewById(R.id.rv);  
  23.         mEditText = (EditText) findViewById(R.id.et);  
  24.   
  25.         mRecyclerView.setLayoutManager(new LinearLayoutManager(context));  
  26.         adapter = new ChatAdapter(context, list);  
  27.         mRecyclerView.setAdapter(adapter);  
  28.   
  29.         clientThread = new ClientThread(handler);  
  30.         new Thread(clientThread).start();  
  31.   
  32.         //点击发送提交数据到服务器  
  33.         mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {  
  34.             @Override  
  35.             public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {  
  36.                 if (actionId == EditorInfo.IME_ACTION_SEND) {  
  37.                     Toast.makeText(context, "提交", Toast.LENGTH_SHORT).show();  
  38.                     Message msg = new Message();  
  39.                     msg.what = 0;  
  40.                     msg.obj = mEditText.getText().toString().trim();  
  41.                     clientThread.revHandler.sendMessage(msg);  
  42.                     mEditText.setText("");  
  43.                 }  
  44.                 return false;  
  45.             }  
  46.         });  
  47.     }  
  48. }  
这里ClientThread是用来处理一些与网络相关的一些操作,主线程当然不能执行耗时的任务。

这里定义的Handler是用来接收显示更新界面的,因为当收到服务器的数据是在子线程中,更新界面操作需要在主线程中,所以需要这个Handler来处理。

当点击发送时,就把消息发送给服务器

ClientThread

[java]  view plain  copy
  1. public class ClientThread implements Runnable {  
  2.     private static final String TAG = "lzy";  
  3.     private OutputStream os;  
  4.     private BufferedReader br;  
  5.     private Socket socket;  
  6.     //用于向UI发送消息  
  7.     private Handler handler;  
  8.     //接收UI线程的消息(当用户点击发送)  
  9.     public Handler revHandler;  
  10.     public ClientThread(Handler handler) {  
  11.         this.handler = handler;  
  12.     }  
  13.     @Override  
  14.     public void run() {  
  15.         //创建一个无连接的Socket  
  16.         socket = new Socket();  
  17.         try {  
  18.             //连接到指定的IP和端口号,并指定10s的超时时间  
  19.             socket.connect(new InetSocketAddress("172.16.7.234"3000), 10000);  
  20.             //接收服务端的数据  
  21.             br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));  
  22.             //向服务端发送数据  
  23.             os = socket.getOutputStream();  
  24.   
  25.             //读取数据会阻塞,所以创建一个线程来读取  
  26.             new Thread(new Runnable() {  
  27.                 @Override  
  28.                 public void run() {  
  29.                     //接收服务器的消息,发送显示在主界面  
  30.                     String content;  
  31.                     try {  
  32.                         while ((content = br.readLine()) != null) {  
  33.                             Message msg = new Message();  
  34.                             msg.what = 1;  
  35.                             msg.obj = content;  
  36.                             handler.sendMessage(msg);  
  37.                         }  
  38.                     }  catch  (IOException e){  
  39.                         e.printStackTrace();  
  40.                     }  
  41.                 }  
  42.             })。开始();  
  43.             //非UI线程,自己创建  
  44.             Looper.prepare();  
  45.             revHandler =  new  Handler(){  
  46.                 @覆盖  
  47.                 public void  handleMessage(Message msg){   
  48.                     //将用户输入的内容写入到服务器,并在前面加上手机型号  
  49.                     尝试 {  
  50.                         os.write((Build.MODEL +  “:”  +(msg.obj)+  “\ n” ).getBytes(“utf-8” ));  
  51.                     }  catch  (IOException e){  
  52.                         e.printStackTrace();  
  53.                     }  
  54.                 }  
  55.             };  
  56.             Looper.loop();  
  57.         }  catch  (IOException e){  
  58.             e.printStackTrace();  
  59.         }  
  60.     }  
  61. }  

你可能感兴趣的