TCP(传输控制协议)和UDP(用户数据报协议是网络体系结构TCP / IP模型中传输层一层中的两个不同的通信协议。
1.客户端发送一个SYN段(同步序号)指明客户打算连接的服务器端口,以及
初始序列号(ISN)。2.服务器发回包含服务器的初始序号的SYN报文段作为应答。同时,将确认序号(ACK)设置为客户的ISN加1以对客户的SYN报文段进行确认。一个SYN将占用一个序号
。3.客户必须确认序号设置为服务器的ISN加1对服务器的SYN报文段进行确认。
。1.TCP提供以认可的方式显式地创建和终止连接
。2.TCP保证可靠的,顺序的(数据包以发送的顺序接收)以及不会重复的数据传输
3.TCP处理流控制
。4 . 允许数据优先
5.如果数据没有传送到,则TCP套接口返回一个出错状态条件
。6.TCP通过保持连续并将数据块分成更小的分片来处理大数据块.-无需程序员知道
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():返回该插座对象对应的输出流,让程序通过该输出流向插座写入数据
一个简单的实例
服务端
- public static void main(String [] args) throws IOException {
- //创建一个ServerSocket的用于监听客户端的请求
- ServerSocket serverSocket = new ServerSocket(80 80);
- while (true ){
- //当接收到客户端的请求时,产生一个对应的插座
- 套接字socket = serverSocket.accept();
- OutputStream os = socket.getOutputStream();
- os.write("jkghjgjhg \n".getBytes("utf-8"));
- os.close();
- socket.close();
- }
- }
客户端
- public class MainActivity extends AppCompatActivity {
- private static final String TAG = "lzy";
- private Context context;
- private TextView mTextView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- context = this;
- mTextView = (TextView) findViewById(R.id.tv);
- }
- public void bt(View view) {
- new Thread() {
- @Override
- public void run() {
- try {
- Socket socket = new Socket("172.16.7.234", 3000);
- BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"utf-8"));
- String line = br.readLine();
- Log.i(TAG, "读取数据:" + line);
- br.close();
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }.start();
- }
- }
设置Socket的超时时间
setSoTimeout(int timeout) :设置Socket的超时时间,不能小于0,如果使用Socket进行读写操作完成之前已经超出了这个时间,那么就会抛出SocketException。
设置Socket连接服务器超时时间
- //创建一个无连接的Socket
- Socket socket = new Socket();
- //让Socket连接到远程服务器,如果经过1秒没有连接到则认为超时
- socket.connect(new InetSocketAddress(host,port),1000);
再写一个聊天室的小应用来看看Socket的使用
服务端
在Android Studio创建一个服务端,点击File->New->New Module,创建一个Java Library
- public class MyServer {
- public static List
listSocket = new ArrayList<>(); - public static void main(String[] args) throws IOException {
- ServerSocket serverSocket = new ServerSocket(808);
- while (true) {
- //等待连接
- Socket socket = serverSocket.accept();
- System.out.println("a target online");
- listSocket.add(socket);
- //每当客户端连接成功 后启动一条线程为该客户端服务
- new Thread(new ServerThread(socket)).start();
- }
- }
- }
listSocket是用于保存客户端连接后产生的Socket对象。
这段代码首先是在8080端口创建一个ServerSocket,然后是一个while的死循环调用ServerSocket的accept方法不停的接收来自客户端的连接请求,然后为这个客户端开启一个线程为其服务
- public class ServerThread implements Runnable {
- private Socket socket;
- private BufferedReader br;
- public ServerThread(Socket socket) throws IOException {
- this.socket = socket;
- br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
- }
- @Override
- public void run() {
- String content;
- //不断把客服端的数据读取出来
- while ((content = readFromClient()) != null) {
- //把收到的消息遍历发给每一个 连接了的客户端
- for (Iterator
iterator = MyServer.listSocket.iterator(); iterator.hasNext(); ) { - Socket socket = iterator.next();
- //打印出客服端的Ip和端口号
- System.out.println("IP:" + socket.getInetAddress() + " port:" + socket.getPort());
- try {
- OutputStream os = socket.getOutputStream();
- os.write((content + "\n").getBytes("utf-8"));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- private String readFromClient() {
- try {
- return br.readLine();
- } catch (IOException e) {
- e.printStackTrace();
- MyServer.listSocket.remove(socket);
- }
- return null;
- }
- }
在这个线程中把Socket的数据读到BufferedReader里面,然后通过一个while循环把消息读取来遍历发送给每一个连接了的客户端。
readFromClient用来读取接收到的客户端数据,如果发生了异常就把这个Socket移除。
客户端
MainActivity
- public class MainActivity extends AppCompatActivity {
- private static final String TAG = "lzy";
- private List
list = new ArrayList<>(); - private RecyclerView mRecyclerView;
- private Context context;
- private ChatAdapter adapter;
- private EditText mEditText;
- private ClientThread clientThread;
- //用于发送接收到的服务端的消息,显示在界面上
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- list.add(msg.obj.toString());
- adapter.notifyDataSetChanged();
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- context = this;
- mRecyclerView = (RecyclerView) findViewById(R.id.rv);
- mEditText = (EditText) findViewById(R.id.et);
- mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
- adapter = new ChatAdapter(context, list);
- mRecyclerView.setAdapter(adapter);
- clientThread = new ClientThread(handler);
- new Thread(clientThread).start();
- //点击发送提交数据到服务器
- mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_SEND) {
- Toast.makeText(context, "提交", Toast.LENGTH_SHORT).show();
- Message msg = new Message();
- msg.what = 0;
- msg.obj = mEditText.getText().toString().trim();
- clientThread.revHandler.sendMessage(msg);
- mEditText.setText("");
- }
- return false;
- }
- });
- }
- }
这里定义的Handler是用来接收显示更新界面的,因为当收到服务器的数据是在子线程中,更新界面操作需要在主线程中,所以需要这个Handler来处理。
当点击发送时,就把消息发送给服务器
ClientThread
- public class ClientThread implements Runnable {
- private static final String TAG = "lzy";
- private OutputStream os;
- private BufferedReader br;
- private Socket socket;
- //用于向UI发送消息
- private Handler handler;
- //接收UI线程的消息(当用户点击发送)
- public Handler revHandler;
- public ClientThread(Handler handler) {
- this.handler = handler;
- }
- @Override
- public void run() {
- //创建一个无连接的Socket
- socket = new Socket();
- try {
- //连接到指定的IP和端口号,并指定10s的超时时间
- socket.connect(new InetSocketAddress("172.16.7.234", 3000), 10000);
- //接收服务端的数据
- br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
- //向服务端发送数据
- os = socket.getOutputStream();
- //读取数据会阻塞,所以创建一个线程来读取
- new Thread(new Runnable() {
- @Override
- public void run() {
- //接收服务器的消息,发送显示在主界面
- String content;
- try {
- while ((content = br.readLine()) != null) {
- Message msg = new Message();
- msg.what = 1;
- msg.obj = content;
- handler.sendMessage(msg);
- }
- } catch (IOException e){
- e.printStackTrace();
- }
- }
- })。开始();
- //非UI线程,自己创建
- Looper.prepare();
- revHandler = new Handler(){
- @覆盖
- public void handleMessage(Message msg){
- //将用户输入的内容写入到服务器,并在前面加上手机型号
- 尝试 {
- os.write((Build.MODEL + “:” +(msg.obj)+ “\ n” ).getBytes(“utf-8” ));
- } catch (IOException e){
- e.printStackTrace();
- }
- }
- };
- Looper.loop();
- } catch (IOException e){
- e.printStackTrace();
- }
- }
- }