当前位置:   article > 正文

Java Socket编程之TCP协议_stocket

stocket

1、概述


  网络编程,又称为Socket编程,简单来讲就是通讯的两个端点都是Socket服务,而Socket之间的数据传输本质上都是IO流。而由于网际层中不同的传输协议,主要指TCP与UDP协议,导致不同的Socket编程的方式,Java为它们提供的API实现是不同的类,但是它们都属于Socket编程的一种,所以都遵循Socket编程的基本特点。


2、TCP协议的特性。


  虽然关于TCP协议的基本特性与UDP协议的差异,想必都比较清楚,但是这里还是要简单回顾一下,因为有助于理解Java中关于TCP协议编程的一些知识点。

  主要的一点:TCP协议在数据发送前,需要通过三次握手建立连接,形成稳定的数据通道,所以TCP数据的发送与UDP不同,UDP是通过Socket服务对象来发送包数据,从而实现数据的传递,而TCP由于事先建立了连接,形成数据通道,这数据通道本质上就是IO流,而且是输入和输出的双向流。


3、Java API中UDP编程知识点。


1、Java API为TCP协议提供的类分为Socket和ServerSocket,可以简单的理解为客户端Socket和服务端Socket,更具体的讲Socket对象是主动发起连接请求的,而ServerSocket对象是等待接收连接请求的。


2、那么客户端什么时候发起连接请求呢?

  Java API中提供了两种方式,第一种是最简单和最常用的,就是通过Socket(InetAddress address,int port)Socket(String host,int port)构造函数创建Socket对象时,Socket服务将向指定的IP地址和端口号发送连接请求;第二种,是通过无参构造函数创建Socket对象,这是面向无连接的,也就是不会发起连接请求,然后再调用voidconnect(SocketAddress endpoint)或者void connect(SocketAddress endpoint,int timeout)方法返回后,将发送连接请求。那么SocketAddress类是什么类呢?通过API文档会发现,它有一个子类为InetAddressSocket,该类其实提供了InetAddress(InetAddress addr,int port)InetAddress(String host,int port)两个构造函数,可以完成对IP地址和端口的封装,而第二个void connect(SocketAddress endpoint,int timeout)方法,第二个参数是用来指定连接超时的时间,也就是说,当向服务端发出连接请求,超出指定时间服务端无响应,则抛出SocketTimeoutException异常,可以捕获这个异常,做针对处理。


3、服务端怎么接收连接请求?

  首先需要通过ServerSocket(int port)构造函数创建服务端Socket对象,参数接收一个端口号,表示该服务绑定该端口号;但此时该Socket服务并不会接收客户端的请求,而是要通过调用Socket accept()方法,该方法执行后,将侦听端口的连接请求,如果侦听不到连接请求,该方法是阻塞式方法,程序挂起;如果侦听到,它会创建一个Socket对象,并指定该Socket对象与客户端Socket通迅,并且accept()方法会返回这个Socket对象。


4、怎么实现数据的传输?

  由于客户端和服务端建立连接以后,形式了IO流通道,而Socket类提供了OutputStream getOutputStream()InputStream getInputStream()方法可以返回输入和输出流,拿到了输入和输出流,就可以充分的使用各种IO流对象实现数据的输入和输出了。

5、Socket和ServerSocket提供了close()方法关闭资源。

6、其他一些常用方法。

InetAddress getInetAddress(),返回Socket连接的地址。

void shutdownOutput(),给Socket输出流添加结束标志,可以让读取该流的read()方法结束。

setSoTimeout(int timeout),设置当通过read()方法读取该Socket流时,如果读取的时间超过指定时间,则报SocketTimeoutException异常。


3、代码示例


1、最简单的代码。


客户端:

  1. package com.example.network;
  2. import java.io.IOException;
  3. import java.io.OutputStream;
  4. import java.net.Socket;
  5. public class TCPClient {
  6. public static void main(String[] args) throws IOException {
  7. //1、创建Socket服务,指定连接的地址和端口号,创建成功将发出连接请求
  8. Socket s = new Socket("127.0.0.1",4000);
  9. //2、连接成功后,将形成数据通道,即输入和输出IO流,可以通过getInpuStream()和getOutputStream()方法获取IO流对象。
  10. OutputStream out = s.getOutputStream();
  11. //3、获取IO对象以后,就可以通过操作IO流来对数据进行读写。
  12. out.write("Hello,TCPServer!".getBytes());
  13. //4.关闭流
  14. s.close();
  15. }
  16. }

服务端:

  1. package com.example.network;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. public class TCPServer {
  7. public static void main(String[] args) throws IOException {
  8. //1、创建服务端Socket对象,绑定端口号。
  9. ServerSocket ss = new ServerSocket(4000);
  10. //2、调用accept()方法来侦听端口号是否有连接请求,有,则返回一个socket对象,该socket将会与客户端的socket形成数据通道;如果没有,则该方法一直处于阻塞状态。
  11. Socket s = ss.accept();
  12. //3、有了与客户端连接的socket,同样我们可以返回输入和输出流,而客户端socket服务的输出流对应是服务端socket对象的输入流。
  13. InputStream in = s.getInputStream();
  14. byte[] buf = new byte[1024];
  15. int len = in.read(buf);
  16. System.out.println(new String(buf,0,len));
  17. //4.关闭socket流
  18. s.close();
  19. ss.close();
  20. }
  21. }


2、手机客户端访问服务端


服务端:


  1. package com.example.network;
  2. import java.io.IOException;
  3. import java.io.OutputStream;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. public class AndroidTCPServer {
  7. public static void main(String[] args) throws IOException {
  8. ServerSocket ss = new ServerSocket(4001);
  9. Socket s = ss.accept();
  10. OutputStream out = s.getOutputStream();
  11. out.write("你好,安卓客户端!".getBytes("utf-8"));
  12. s.close();
  13. ss.close();
  14. }
  15. }


客户端:

  1. package com.noodles.networktest;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.widget.TextView;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.net.Socket;
  10. public class AndroidTCPClientActivity extends Activity {
  11. public static final int UPDATE_TEXT = 1;
  12. private TextView textView;
  13. private Handler handler = new Handler(){
  14. @Override
  15. public void handleMessage(Message msg) {
  16. switch (msg.what){
  17. case UPDATE_TEXT:
  18. String text = (String) msg.obj;
  19. textView.setText(text);
  20. }
  21. }
  22. };
  23. @Override
  24. protected void onCreate(Bundle savedInstanceState) {
  25. super.onCreate(savedInstanceState);
  26. setContentView(R.layout.tcp_client_activity);
  27. textView = (TextView) findViewById(R.id.text_view);
  28. connectToTCPServer();
  29. }
  30. private void connectToTCPServer() {
  31. new Thread(new Runnable() {
  32. @Override
  33. public void run() {
  34. try {
  35. Socket s = new Socket("192.168.1.101",4001);
  36. InputStream in = s.getInputStream();
  37. byte[] buf = new byte[1024];
  38. int len = in.read(buf);
  39. String text = new String(buf,0,len);
  40. Message msg = new Message();
  41. msg.what = UPDATE_TEXT;
  42. msg.obj = text;
  43. handler.sendMessage(msg);
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. }).start();
  49. }
  50. }

结果:




注意点:

1、在服务端中,通过IO输出流写入中文时,没有使用包装流,并且通过String方法getBytes()指定了转换字符集utf-8,因为本人是Windows操作系统,当java程序中涉及到字符转换的时候,默认是使用的平台的字符集,而Windows默认字符集是GBK,但是Android系统是linux核心,使用的是utf-8,所以如果不指定相同的字符集,就会出现乱码。而现在字符集一般都是使用utf-8或者utf-16。

2、Android使用了多线程机制,因为Android不能在主线程进行网络访问。

3、因为需要对主线程的UI进行更新,所以使用Android平台提供的异步信息处理机制之一Handler。


3、多线程


  先思考一个问题,服务端往往是需要处理多个客户端请求的,比如服务端提供了一个2G的游戏程序资源让去用户下载,在下载资源的过程中其实服务端在向输出流中写入数据,但是这个过程可能是漫长,可能需要半个小时,那么在下载过程中,别的用户是没办法下载的,因为服务端程序在执行IO流的操作,没办法执行accept()方法,就不能接收其它客户端的请求,所以我们需要把耗时IO流操作放入子线程中,这样主线程用来接收请求,而一旦有客户端请求,就开辟一个线程去做耗时IO流处理。


简答的手机聊天程序


服务端:

  1. package com.example.network;
  2. import java.io.IOException;
  3. import java.net.ServerSocket;
  4. import java.net.Socket;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. public class ChatServer {
  8. static List<Socket> list = new ArrayList<Socket>();
  9. public static void main(String[] args) throws IOException {
  10. ServerSocket ss = new ServerSocket(4002);
  11. while(true){
  12. Socket s = ss.accept();
  13. String ip = s.getInetAddress().getHostAddress();
  14. System.out.println(ip+" is connected......");
  15. list.add(s);
  16. new Thread(new Chat(s)).start();
  17. }
  18. }
  19. }

服务端流操作:

  1. package com.example.network;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.net.Socket;
  6. import java.util.Iterator;
  7. public class Chat implements Runnable{
  8. private Socket s;
  9. Chat(Socket s){
  10. this.s = s;
  11. }
  12. @Override
  13. public void run() {
  14. try {
  15. InputStream in = s.getInputStream();
  16. byte[] buf = new byte[1024];
  17. int len;
  18. while((len = in.read(buf))!= -1){
  19. String str = new String(buf,0,len,"utf-8");
  20. for(Iterator<Socket> iterator =ChatServer.list.iterator();iterator.hasNext();){
  21. try{
  22. Socket s = iterator.next();
  23. String ip = s.getInetAddress().getHostAddress();
  24. System.out.println("size:"+ChatServer.list.size() +"," +ip+":"+str);
  25. OutputStream out = s.getOutputStream();
  26. out.write((ip+":"+str).getBytes("utf-8"));
  27. }catch(IOException e){
  28. iterator.remove();
  29. System.out.println(ChatServer.list);
  30. }
  31. }
  32. }
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }

手机端:

  1. package com.noodles.networktest;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.text.TextUtils;
  7. import android.view.View;
  8. import android.widget.Button;
  9. import android.widget.EditText;
  10. import android.widget.TextView;
  11. import java.io.IOException;
  12. public class ChatActivity extends Activity {
  13. static final int SEND_TEXT = 1;
  14. static final int UPDATE_TEXT = 2;
  15. private TextView textView;
  16. private EditText inputText;
  17. private Button send;
  18. private Handler handler = new Handler() {
  19. @Override
  20. public void handleMessage(Message msg) {
  21. switch (msg.what) {
  22. case UPDATE_TEXT:
  23. textView.append(System.getProperty("line.separator")+(String)(msg.obj));
  24. break;
  25. default:
  26. break;
  27. }
  28. }
  29. };
  30. private ChatClient chatClient;
  31. @Override
  32. protected void onCreate(Bundle savedInstanceState) {
  33. super.onCreate(savedInstanceState);
  34. setContentView(R.layout.chat_activity);
  35. textView = (TextView) findViewById(R.id.text_view);
  36. inputText = (EditText) findViewById(R.id.input_text);
  37. send = (Button) findViewById(R.id.send);
  38. chatClient = new ChatClient(handler);
  39. new Thread(chatClient).start();
  40. send.setOnClickListener(new View.OnClickListener() {
  41. @Override
  42. public void onClick(View v) {
  43. String text = inputText.getText().toString();
  44. if (!TextUtils.isEmpty(text)) {
  45. Message message = new Message();
  46. message.what = SEND_TEXT;
  47. message.obj = text;
  48. chatClient.mHandler.sendMessage(message);
  49. inputText.setText("");
  50. }
  51. }
  52. });
  53. }
  54. @Override
  55. protected void onDestroy() {
  56. super.onDestroy();
  57. try {
  58. chatClient.s.close();
  59. } catch (IOException e) {
  60. e.printStackTrace();
  61. }
  62. }
  63. }

  1. package com.noodles.networktest;
  2. import android.os.Handler;
  3. import android.os.Looper;
  4. import android.os.Message;
  5. import android.util.Log;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.OutputStream;
  9. import java.net.InetSocketAddress;
  10. import java.net.Socket;
  11. class ChatClient implements Runnable {
  12. Handler mHandler;
  13. private Handler uiHandler;
  14. private InputStream in;
  15. private OutputStream out;
  16. Socket s;
  17. ChatClient(Handler uiHandler) {
  18. this.uiHandler = uiHandler;
  19. }
  20. @Override
  21. public void run() {
  22. try {
  23. s = new Socket();
  24. s.connect(new InetSocketAddress("192.168.1.101", 4002), 5000);
  25. in = s.getInputStream();
  26. out = s.getOutputStream();
  27. new Thread() {
  28. @Override
  29. public void run() {
  30. byte[] buf = new byte[1024];
  31. int len;
  32. try {
  33. while ((len = in.read(buf)) != -1) {
  34. String text = new String(buf, 0, len, "utf-8");
  35. Log.d("ChatActivity", text);
  36. Message msg = new Message();
  37. msg.what = ChatActivity.UPDATE_TEXT;
  38. msg.obj = text;
  39. uiHandler.sendMessage(msg);
  40. }
  41. } catch (IOException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. }.start();
  46. Looper.prepare();
  47. mHandler = new Handler() {
  48. @Override
  49. public void handleMessage(Message msg) {
  50. switch (msg.what) {
  51. case ChatActivity.SEND_TEXT:
  52. String text = (String)(msg.obj);
  53. try {
  54. out.write(text.getBytes());
  55. } catch (IOException e) {
  56. e.printStackTrace();
  57. }
  58. break;
  59. default:
  60. break;
  61. }
  62. }
  63. };
  64. Looper.loop();
  65. } catch (IOException e) {
  66. e.printStackTrace();
  67. }
  68. }
  69. }

结果:


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/704335
推荐阅读
相关标签
  

闽ICP备14008679号