JAVA基础学习之TCP网络编程

TCP/IP

Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议。

TCP/IP协议的基本传输单位是数据包(datagram)。

TCP协议,是专门设计用于在不可靠的因特网上提供可靠的,端到端的字节流通信协议。如果IP数据包中有已经封好的TCP数据包,那么IP将把它们向‘上’传送到TCP层。TCP将包排序并进行错误检查,同时实现虚电路间的连接(通过三次握手实现)。TCP数据包中包括序号和确认,所以未按照顺序收到的包可以被排序,而损坏的包可以被重传。
TCP是一种面向连接的通信协议。TCP连接提供两台计算机之间的可靠无差错的字节流数据传输。

主要特点:
(1)TCP/IP协议不依赖于任何特定的计算机硬件或操作系统,提供开放的协议标准,即使不考虑Internet,TCP/IP协议也获得了广泛的支持。所以TCP/IP协议成为一种联合各种硬件和软件的实用系统。
(2)TCP/IP协议并不依赖于特定的网络传输硬件,所以TCP/IP协议能够集成各种各样的网络。
(3)统一的网络地址分配方案,使得整个TCP/IP设备在网中都具有唯一的地址
(4)标准化的高层协议,可以提供多种可靠的用户服务。

TCP/IP模型的主要缺点有:
第一,它在服务、接口与协议的区别上就不是很清楚。一个好的软件工程应该将功能与实现方法区分开来,TCP/IP恰恰没有很好地做到这点,就使得TCP/IP参考模型对于使用新的技术的指导意义是不够的。TCP/IP参考模型不适合于其他非TCP/IP协议簇。
第二,主机-网络层本身并不是实际的一层,它定义了网络层与数据链路层的接口。物理层与数据链路层的划分是必要和合理的,一个好的参考模型应该将它们区分开,而TCP/IP参考模型却没有做到这点。

开发步骤

创建socket;
打开连接到socket的输入/输出流;
按照一定的协议对socket进行读/写操作;
关闭socket;

服务器 :
打开连接(得到客户端的socket ) 有一个客户端连接上来
得到输出流 (打开通信通道)
关闭连接
客户端:
打开连接 创建socket 要有地址(IP,port)
得到的输入流 ( 打开通信通道)
关闭连接

这里写图片描述

示例:

例子使用WindowBuilder图形界面进行编写,实现简单的一对一聊天(由于TCP实现多对多聊天有一定难度,没有Demo)
效果:
这里写图片描述

客户端:

package com.ql.Client;

import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JLabel;
import java.awt.Font;

public class Client implements ActionListener{

    private JFrame frame;
    private JTextField txtStr;
    private JTextArea txtAr;
    private JButton btnConnect, btnSend;
    private JTextField txtAddress;

    private String address = "127.0.0.1";
    public  boolean bConnected = false;//连接标识位

    Socket socket = null;//连接套接字
    DataInputStream in=null;//输入流
    DataOutputStream out=null;//输出流
    String name;//昵称

    Thread tRecv; //接收数据线程
    private JTextField txtName;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Client window = new Client();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public Client() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent arg0) {
                disconnect();//程序关闭,关闭连接
            }
        });
        frame.setBounds(100, 100, 556, 376);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel();
        frame.getContentPane().add(panel, BorderLayout.CENTER);
        panel.setLayout(null);

        JPanel panel_1 = new JPanel();
        panel_1.setBounds(0, 291, 540, 47);
        panel.add(panel_1);
        panel_1.setLayout(null);

        txtStr = new JTextField();
        txtStr.setBounds(67, 15, 348, 23);
        panel_1.add(txtStr);
        txtStr.setColumns(10);

        btnSend = new JButton("\u53D1\u9001");
        btnSend.setBounds(427, 14, 101, 25);
        btnSend.addActionListener(this);
        panel_1.add(btnSend);

        JLabel label = new JLabel("\u8BF7\u8F93\u5165\uFF1A");
        label.setFont(new Font("宋体", Font.PLAIN, 12));
        label.setBounds(12, 18, 58, 17);
        panel_1.add(label);

        JPanel panel_2 = new JPanel();
        panel_2.setBounds(0, 31, 540, 262);
        panel.add(panel_2);
        panel_2.setLayout(null);

        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBounds(0, 0, 540, 262);
        panel_2.add(scrollPane);

        txtAr = new JTextArea();
        scrollPane.setViewportView(txtAr);

        JPanel panel_3 = new JPanel();
        panel_3.setBounds(0, 0, 540, 30);
        panel.add(panel_3);
        panel_3.setLayout(null);

        txtAddress = new JTextField();
        txtAddress.setText("127.0.0.1");
        txtAddress.setBounds(88, 5, 184, 23);
        panel_3.add(txtAddress);
        txtAddress.setColumns(10);

        JLabel lblNewLabel = new JLabel("\u670D\u52A1\u5668IP:");
        lblNewLabel.setFont(new Font("宋体", Font.PLAIN, 12));
        lblNewLabel.setBounds(12, 8, 58, 17);
        panel_3.add(lblNewLabel);

        btnConnect = new JButton("\u8FDE\u63A5");
        btnConnect.setFont(new Font("宋体", Font.PLAIN, 12));
        btnConnect.setBounds(284, 4, 101, 25);
        btnConnect.addActionListener(this);
        panel_3.add(btnConnect);

        txtName = new JTextField();
        txtName.setText("\u5BA2\u6237\u7AEF0");
        txtName.setBounds(435, 6, 95, 21);
        panel_3.add(txtName);
        txtName.setColumns(10);

        JLabel lblNewLabel_1 = new JLabel("\u6635\u79F0\uFF1A");
        lblNewLabel_1.setBounds(395, 9, 42, 15);
        panel_3.add(lblNewLabel_1);

    }

    /**
     * 发送数据方法
     * @param str
     */
    protected void send(String str) {
        // TODO Auto-generated method stub
        try {
            out.writeUTF(name + ":" +str);//向服务器写数据
            txtAr.setText(txtAr.getText() + "您:" + str + "\n");
            out.flush();//清空缓冲区数据
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 用于接收数据的线程
     * @author qinlang
     *
     */
    class RecvThread implements Runnable{

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(true){
                try {
                    while(bConnected) {
                        String str = in.readUTF();//读取数据
                        txtAr.setText(txtAr.getText() + str +"\n");         
                    }
                } catch (SocketException e) {
//                  System.out.println("退出了,bye!");
                } catch (EOFException e) {
//                  System.out.println("退出了,bye - bye!");
                } catch (IOException e) {
                    e.printStackTrace();
                }   
            }
        }
    }

    /**
     * 建立连接方法
     */
    public void connect() {
        try {
            socket = new Socket(address, 8888);
            out = new DataOutputStream(socket.getOutputStream());//数据输出流
            in = new DataInputStream(socket.getInputStream());//数据输入流
            txtAr.setText(txtAr.getText() + "\n已连接...\n");
            bConnected = true;
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            txtAr.setText(txtAr.getText() + "\n连接失败,请关闭后重试!\n");
        }
        tRecv =  new Thread(new RecvThread(), name);//实例化接收线程
    }

    /**
     * 关闭连接方法
     */
    public void disconnect() {
        try {
            if (out != null)
                out.close();
            if (in != null)
                in.close();
            if (socket != null)
                socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // 按钮按键处理
        if(e.getSource() == btnSend){//发送
            String str = txtStr.getText().trim();

            send(str);
        }
        if(e.getSource() == btnConnect){//连接
            address = txtAddress.getText().trim();
            if(!"".equals(address) && !"".equals(txtName.getText().trim())){
                name = txtName.getText().trim();
                connect();//封装的连接方法
                tRecv.start();//启动接收线程
            }else{
                txtAr.setText(txtAr.getText() + "\n请输入需要连接服务器的IP或昵称!");
            }
        }
    }
}

服务器:
下面的设计,服务器为两两客户端开辟一个单独线程,从而达到一对一进行通信。

package com.ql.Server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class OneByOneServer {
    static Thread ttThread = null;

    /**
     * @param args
     */
    public static void main(String[] args) {
        Socket you = null, me = null;
        ServerSocket server = null;

        while (true) {
            try {
                server = new ServerSocket(8888);
            } catch (IOException e1) {
                 e1.printStackTrace();
            }
            try {
                you = server.accept();//建立连接等待
                me = server.accept();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (you != null){//默认当对方上线才启动线程,服务器为两两客户端开辟一个线程
                ttThread = new ServerThread(you, me);
                ttThread.start();
            }else{//否则继续等待
                continue;
            }
        }
    }
}

class ServerThread extends Thread {
    Socket you, me;
    DataOutputStream youOut = null;
    DataInputStream youIn = null;
    DataOutputStream meOut = null;
    DataInputStream meIn = null;

    public ServerThread(Socket you, Socket me) {
        this.you = you;
        this.me = me;
        try {
            //输入输出流
            youIn = new DataInputStream(you.getInputStream());
            youOut = new DataOutputStream(you.getOutputStream());
            meIn = new DataInputStream(me.getInputStream());
            meOut = new DataOutputStream(me.getOutputStream());
        } catch (IOException e) {
        }
    }

    @Override
    public void run() {
        while (true) {
            //数据发送,读取自己的数据发送给对方,对方的数据发送给自己
            try {
                String youInString = /*you.getInetAddress() + ":" + */youIn.readUTF();
                meOut.writeUTF(youInString);
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    meOut.writeUTF("对方已离开。。。");
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            try {
                String meInString = /*me.getInetAddress() + ":" +*/ meIn.readUTF();
                youOut.writeUTF(meInString);
            } catch (Exception e) {
                try {
                    youOut.writeUTF("对方已离开。。。");
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值