文章目錄
1、socket是啥
套接字(socket)是一個抽象層,應用程序可以通過它發送或接收數據,可對其進行像對文件一樣的打開、讀寫和關閉等操作。套接字允許應用程序將I/O插入到網絡中,並與網絡中的其他應用程序進行通信。網絡套接字是IP地址與端口的組合。–這波百度百科怎麼說
來點直觀的吧,不然不會大夥還是不會!!
win+r ->輸入:cmd ->在命令臺輸入:netstat -ano 就可以看到你當前電腦的所有套接字了哦,也就是socket == ip號+端口號!!!
下面這個socket建立雙向連接之後,雙方再進行通信的一個簡單概念模型。
2、爲什麼會socket這個東西出現呢?
- 爲了計算機可以創建更多的對外連接。
你想啊,你的電腦有一個自己的ip何端口,先不管這個ip是局域網內還是公網,你要想建立對外的連接,你只有一個ip地址,難道你想只能對外建立一個連接嗎?當然不行啦,所有,我們將ip+端口構成的套接字,作爲一個計算機對外連接的一個標記或是說通道。
3、socket這麼厲害,那我們怎麼使用它呢?
下面就是java代碼時間,我使用TCP的方式建立socket連接。
先來來個簡單的demo,這種模式只能一發一收,因爲每次輸入的地方會有阻塞,沒啥好講的,看註釋。
3.1、simpleDemo
3.1.1、Server
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String args[]) throws IOException {
ServerSocket serverSocket = null;
try {
//監聽自己的端口
serverSocket = new ServerSocket(6666);
} catch (IOException e) {
e.printStackTrace();
}
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
//開啓本地的socket輸入字節流
BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
//開啓本地的socket輸入的字節流的傳送通道
PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
//開啓客戶端socket的字節流輸入通道
BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//輸入本地的socket需發送的數據
String readline = socketInput.readLine();
while(!readline.equals("")){
socketInputSend.println(readline);
//刷新輸出流,馬上發出自己的該字符串
socketInputSend.flush();
System.out.println("Server:"+readline);
//獲取客戶端socket的輸入數據,同樣會阻塞
System.out.println("Client2:"+socketGetother.readLine());
//輸入阻塞
readline = socketInput.readLine();
}
socketInput.close();
socketGetother.close();
socket.close();
}
}
3.1.2、Client
package com.icitic.mc.Task03.socketChat.simpleDemo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 6666);
//開啓本地的socket輸入字節流
BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
//開啓本地的socket輸入的字節流的傳送通道
PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
//開啓服務器socket的字節流輸入通道
BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//輸入本地的socket需發送的數據
String readline = socketInput.readLine();
while(!readline.equals("")){
socketInputSend.println(readline);
//刷新輸出流,馬上發出自己的該字符串
socketInputSend.flush();
System.out.println("Client2:"+readline);
//獲取服務器socket的輸入數據,同樣會阻塞
System.out.println("Server:"+socketGetother.readLine());
//輸入阻塞
readline = socketInput.readLine();
}
socketInput.close();
socketGetother.close();
socket.close();
}
}
3.2、ClientToServer
掌握了簡單socket連接之後,這裏我們將客戶端和服務端程序都分兩個線程來執行,即監聽socket連接的另一端使用一個線程,自己輸入一個線程。這樣雙線程雙管齊下就可以實現無縫對話了,不必說不出口了。
3.2.1、Server
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 雙線程-監聽-發送
*/
public class Server{
public static void main(String args[]) throws IOException {
//服務器監聽 自身的 端口
Socket socket = bindSocket();
//開啓本地的socket輸入字節流
BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
//開啓本地的socket輸入的字節流的傳送通道
PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
//開啓客戶端socket的字節流輸入通道
BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//開線程來監聽客戶端消息
getMessage(socketGetother);
//輸入本地的socket需發送的數據,會阻塞
String readline = socketInput.readLine();
while(!readline.equals("Bye")){
//發送出本地消息
sendMessage(socketInputSend,readline,socketInput);
//輸入阻塞
readline = socketInput.readLine();
}
socketClose(socketInput,socketGetother,socket);
}
/**
* 內部自開線程監聽服務器端消息
* @param socketGetother
*/
private static void getMessage(final BufferedReader socketGetother) {
new Thread(new Runnable() {
public void run() {
//獲取客戶端socket的輸入數據,readLine()同樣會阻塞
try {
String readline = socketGetother.readLine();
while(!readline.equals("Bye")){
System.err.println("*************************Client2:"+readline);
readline = socketGetother.readLine();
}
System.out.println("------------------------聊天已結束-------------------------");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 本地發送消息
* @param socketInputSend
* @param readline
* @param socketInput
* @throws IOException
*/
private static void sendMessage(PrintWriter socketInputSend, String readline, BufferedReader socketInput) throws IOException {
//將本地輸入的字節流放入通道
socketInputSend.println(readline);
//刷新輸出流,馬上發出自己的該字符串
socketInputSend.flush();
System.out.println("*************************myself:"+readline);
}
/**
* 關閉輸入輸出和socket
* @param socketInput
* @param socketGetother
* @param socket
* @throws IOException
*/
private static void socketClose(BufferedReader socketInput, BufferedReader socketGetother, Socket socket) throws IOException {
socketInput.close();
socketGetother.close();
socket.close();
}
/**
* 開啓服務器的監聽socket
* @return
*/
private static Socket bindSocket() {
ServerSocket serverSocket = null;
Socket socket = null;
try {
serverSocket = new ServerSocket(6666);
} catch (IOException e) {
e.printStackTrace();
}
try {
socket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(socket);
return socket;
}
}
3.2.2、Client
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* 雙線程-監聽-發送
*/
public class Client {
final static String LOCALHOSt = "127.0.0.1";
final static String VM = "192.168.154.3";
public static void main(String[] args) throws IOException {
Socket socket = new Socket(LOCALHOSt, 6666);
System.out.println(socket);
//開啓本地的socket輸入字節流
BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
//開啓本地的socket輸入的字節流的傳送通道
PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
//開啓服務器socket的字節流輸入通道
BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//開線程來監聽服務器端消息
getMessage(socketGetother);
//輸入本地的socket需發送的數據,readLine()會阻塞
String readline = socketInput.readLine();
while(!readline.equals("Bye")){
//發送出本地消息
sendMessage(socketInputSend,readline,socketInput);
//輸入阻塞
readline = socketInput.readLine();
}
socketClose(socketInput,socketGetother,socket);
}
/**
* 內部自開線程監聽服務器端消息
* @param socketGetother
*/
private static void getMessage(final BufferedReader socketGetother) {
new Thread(new Runnable() {
public void run() {
//獲取服務器socket的輸入數據,readLine()同樣會阻塞
try {
String readline = socketGetother.readLine();
while(!readline.equals("Bye")){
System.err.println("*************************Server:"+readline);
readline = socketGetother.readLine();
}
System.out.println("------------------------聊天已結束-------------------------");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 本地發送消息
* @param socketInputSend
* @param readline
* @param socketInput
* @throws IOException
*/
private static void sendMessage(PrintWriter socketInputSend, String readline, BufferedReader socketInput) throws IOException {
//將本地輸入的字節流放入通道
socketInputSend.println(readline);
//刷新輸出流,馬上發出自己的該字符串
socketInputSend.flush();
System.out.println("*************************myself:"+readline);
}
/**
* 關閉輸入輸出和socket
* @param socketInput
* @param socketGetother
* @param socket
* @throws IOException
*/
private static void socketClose(BufferedReader socketInput, BufferedReader socketGetother, Socket socket) throws IOException {
socketInput.close();
socketGetother.close();
socket.close();
}
}
3.2.3、運行結果
通過結果可以看到,對其加上多線程後,雙方都可以多發多收,家家戶戶都通網了,普天同慶。
3.3、chatRoom
這裏我得說明一下,博主沒那麼多時間使用socket實現聊天室。。我就改了下3.2.1的Server,可以實現多個Client連接,Server可以同時接收所有Client的所有消息,但是Server沒有推送出它接收的所有消息給所有Client,Server只能自己輸入並且一次只能推送給一個Client。多個Client,Server大概使用輪詢的方式實現推送給各個Client,(其實也不是輪詢,是線程競爭的結果)
我就掛出以惡個Server,Client程序和3.2.2是一樣,複製多個就行。
3.3.1、Server
package com.icitic.mc.Task03.socketChat.chatRoom;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 雙線程-監聽-發送
*/
public class Server{
static ServerSocket serverSocket = null;
public static void main(String args[]) throws IOException {
try {
serverSocket = new ServerSocket(6666);
} catch (IOException e) {
e.printStackTrace();
}
for (int i = 0 ; i < 3; i++){
new Thread(new Runnable() {
public void run() {
try {
chatRoom();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
/**
* socket服務器主要邏輯代碼
* @throws IOException
*/
private static void chatRoom() throws IOException {
//服務器監聽 自身的 端口
Socket socket = bindSocket();
//開啓本地的socket輸入字節流
BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
//開啓本地的socket輸入的字節流的傳送通道
PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
//開啓客戶端socket的字節流輸入通道
BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//開線程來監聽客戶端消息
getMessage(socketGetother);
//輸入本地的socket需發送的數據,會阻塞
String readline = socketInput.readLine();
while(!readline.equals("Bye")){
//發送出本地消息
sendMessage(socketInputSend,readline,socketInput);
//輸入阻塞
readline = socketInput.readLine();
}
socketClose(socketInput,socketGetother,socket);
}
/**
* 內部自開線程監聽服務器端消息
* @param socketGetother
*/
private static void getMessage(final BufferedReader socketGetother) {
new Thread(new Runnable() {
public void run() {
//獲取客戶端socket的輸入數據,readLine()同樣會阻塞
try {
String readline = socketGetother.readLine();
while(!readline.equals("Bye")){
System.err.println("*************************Client2:"+readline);
readline = socketGetother.readLine();
}
System.out.println("------------------------聊天已結束-------------------------");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 本地發送消息
* @param socketInputSend
* @param readline
* @param socketInput
* @throws IOException
*/
private static void sendMessage(PrintWriter socketInputSend, String readline, BufferedReader socketInput) throws IOException {
//將本地輸入的字節流放入通道
socketInputSend.println(readline);
//刷新輸出流,馬上發出自己的該字符串
socketInputSend.flush();
System.out.println("*************************myself:"+readline);
}
/**
* 關閉輸入輸出和socket
* @param socketInput
* @param socketGetother
* @param socket
* @throws IOException
*/
private static void socketClose(BufferedReader socketInput, BufferedReader socketGetother, Socket socket) throws IOException {
socketInput.close();
socketGetother.close();
socket.close();
}
/**
* 開啓服務器的監聽socket
* @return
*/
private static Socket bindSocket() {
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(socket);
return socket;
}
}