TCP/IP協(xié)議
IP、TCP、UDP 都是TCP/IP協(xié)議的一部分。而Socket是應(yīng)用層與TCP/IP協(xié)議通信抽象出來的接口。

TCP/IP(Transmission Control Protocol/Internet Protocol)即傳輸控制協(xié)議/網(wǎng)間協(xié)議,是一個工業(yè)標(biāo)準(zhǔn)的協(xié)議集,它是為廣域(WANs)設(shè)計的。包括運輸層、網(wǎng)絡(luò)層、鏈路層。
TCP與UDP的區(qū)別
TCP
- TCP面向連接(三次握手);UDP是無連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接。
- TCP傳輸可靠而UDP不可靠。也就是說,通過TCP連接傳送的數(shù)據(jù),無差錯,不丟失,不重復(fù),且按序到達;而UDP則不保證,可能丟包,也不按順序到達。因此UDP更快,效率高,TCP相反。
- TCP面向字節(jié)流,實際上是TCP把數(shù)據(jù)看成一連串無結(jié)構(gòu)的字節(jié)流。UDP是面向報文的。
- 通信方式:每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信。
- 基于以上這些區(qū)別,因此使用場景不同。TCP用在瀏覽器(Http)、文件傳輸(FTP)、接發(fā)郵件(SMTP,POP)、遠程登錄(telnet、ssh)。UDP用在視頻通話、語音通話等,適用于多播和廣播的應(yīng)用場景。
總之,TCP傳播數(shù)據(jù)準(zhǔn)確但速度較慢,用在文件傳輸?shù)葘?zhǔn)確性要求較高的地方。UDP相反,用在視頻流等大流量對速度要求較高的地方。
TCP和UDP的Socket實現(xiàn)(JAVA)
Java為Socket編程封裝了幾個重要的類。其中Socket和ServerSocket用于TCP通信。DatagramSocket和DatagramPacket用于UDP通信。

基于TCP的Socket編程
Server端
服務(wù)器端首先實例化ServerSocket對象,然后為其綁定一個本機地址,并開始監(jiān)聽。一直阻塞狀態(tài)下等待客戶端請求,當(dāng)獲得客戶端連接請求后,返回一個socket對象。然后用這個socket接收一條消息,并發(fā)送一條消息。代碼如下:
package com.chenxuri.java.network;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by chenxuri on 2017/12/21.
*/
public class SocketTcpServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("Connecting to client ...");
/*
接收客戶端數(shù)據(jù)
*/
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
BufferedReader socketIn = new BufferedReader(new InputStreamReader(inputStream));
String temp;
while ((temp = socketIn.readLine()) != null) {
System.out.println(temp);
}
/*
發(fā)送數(shù)據(jù)
*/
OutputStream outputStream = socket.getOutputStream();
PrintWriter socketOut = new PrintWriter(outputStream);
socketOut.print("Hi,I have received your message!");
socketOut.flush();
socketOut.close();
socketIn.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client端
客戶端首先實例化一個socket對象,用這個對象連接服務(wù)器端。連接成功后,發(fā)送一條消息,然后等待接收一條消息。代碼如下:
package com.chenxuri.java.network;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
* Created by chenxuri on 2017/12/21.
*/
public class SocketTcpClient {
public static void main(String[] args) {
try {
Socket socket = new Socket();
/*
設(shè)置地址和連接超時時間,有的地方說不設(shè)置連接時間可能會造成無限期阻塞。
其實不會,因為操作系統(tǒng)底層有超時限制,windows是20秒。
但仍建議設(shè)置一個較短的時間,尤其是需要頻繁連接的時候。
*/
socket.connect(new InetSocketAddress("127.0.0.1",8888), 5*1000);
//設(shè)置讀取超時時間,該時間若不設(shè)置,服務(wù)器等出現(xiàn)問題時可能會一直處于阻塞狀態(tài)。
socket.setSoTimeout(10*1000);
/*
往服務(wù)端發(fā)送數(shù)據(jù)
*/
OutputStream outputStream = socket.getOutputStream();
PrintWriter socketOut = new PrintWriter(outputStream);
String str = "Hello,I come from client!";
socketOut.write(str);
socketOut.flush();
socket.shutdownOutput(); //半關(guān)閉,告訴服務(wù)端發(fā)送完畢,可以接受輸入過來的數(shù)據(jù)
/*
接收回應(yīng)數(shù)據(jù)
*/
InputStream inputStream = socket.getInputStream();
BufferedReader socketIn = new BufferedReader(new InputStreamReader(inputStream));
String temp;
while ((temp = socketIn.readLine()) != null) {
System.out.println(temp);
}
socketIn.close();
socketOut.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
基于UDP的Socket編程
Server端
服務(wù)器端首先實例化DatagramSocket對象,然后為其綁定一個端口,并開始監(jiān)聽。一直阻塞狀態(tài)下等待從客戶端接收數(shù)據(jù)報。然后從數(shù)據(jù)報中獲取數(shù)據(jù)報的源地址,然后用這個源地址作為目的地址打包一個數(shù)據(jù)報,然后發(fā)送出去。代碼如下:
package com.chenxuri.java.network;
import java.io.*;
import java.net.*;
/**
* Created by chenxuri on 2017/12/21.
*/
public class SocketUdpServer {
public static void main(String[] args) {
try {
DatagramSocket socket = new DatagramSocket(8888);
/*
接收客戶端的數(shù)據(jù)
*/
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
socket.receive(packet);
String receiveStr = new String(bytes);
System.out.println("From client: " + receiveStr);
/*
發(fā)送回應(yīng)數(shù)據(jù)
*/
int port = packet.getPort();
InetAddress addr = packet.getAddress();
String sendStr = "Hello! I'm Server";
byte[] sendBuf = sendStr.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendBuf , sendBuf.length , addr , port );
socket.send(sendPacket);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client端
客戶端首先實例化一個DatagramSocket對象。利用服務(wù)器地址和端口號作為目的地址打包一個數(shù)據(jù)報,并發(fā)送。然后等待從服務(wù)器回復(fù)的數(shù)據(jù)報。代碼如下:
package com.chenxuri.java.network;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
/**
* Created by chenxuri on 2017/12/21.
*/
public class SocketUdpClient {
public static void main(String[] args) {
try {
DatagramSocket socket = new DatagramSocket();
/*
發(fā)送數(shù)據(jù)
*/
String sendStr = "I'm client, this is the message for server.";
byte[] bytes = sendStr.getBytes();
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
packet.setSocketAddress(new InetSocketAddress("127.0.0.1",8888));
socket.send(packet);
/*
接收返回數(shù)據(jù)
*/
byte[] backbuf = new byte[1024];
DatagramPacket backPacket = new DatagramPacket(backbuf, backbuf.length);
socket.receive(backPacket);
System.out.println("From server: "+ new String(backbuf));
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
以上代碼都經(jīng)過親自測試,可以運行。