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();
}
}
}
}
}