內容
1.客戶端和服務器端連接介紹
2.實現點對點對聊
3.實現群聊
一.客戶端和服務器端連接介紹
1.注意點
①一般來講,有時候沒有嚴格意義的服務器端和客戶端
比如A端去連接遠程網絡中的B端,那么此時B端就扮演服務器的角色,A端扮演客戶端的角色
②緩存:當B不在線時,A先發給騰訊的服務器(這里假如是QQ),這里面的內容包括B的IP地址,還有端口號,還有具體的內容,當B上線時,就發給B。這里就相當于做了一個緩存。
2.作為一個客戶端必須提供的兩個內容
①IP地址(通過這個找到某個設備)
②端口號(找到提供服務的程序,比如qq)
3.客戶端和服務器端如何連接(socket)
(1)使用Socket實現連接
Socket的英文原義是“孔”或“插座”。在網絡編程中,網絡上的兩個程序通過一個雙向的通信連接實現數據的交換,這個連接的一端稱為一個socket。Socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網絡開發所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通信的能力。 Socket實質上提供了進程通信的端點。進程通信之前,雙方首先必須各自創建一個端點,否則是沒有辦法建立聯系并相互通信的。正如打電話之前,雙方必須各自擁有一臺電話機一樣。
而serversocket 建立的是socket的服務端,socket建立的是客戶端。所以服務器端使用ServerSocket
客戶端使用Socket
(2)使用socket實現網絡編程的步驟
1.分別在服務器端和客戶端完成ServerSocket和Socket的創建,這也是我們實現網絡通信的基礎
2.打開連接到Socket的相關的輸入輸出流,進行數據的通信
3.按照協議對Socket進行讀寫操作
4.在通信完成以后關閉輸入輸出流,關閉socket
如果分兩步的話,就是
第一步是監聽(等待數據發送過來),用來接收數據,需要指定監聽的端口號
第二步是發送,需要指定發送到哪個計算機(IP地址),需要指定發送到這個計算機的哪個端口。
(3)具體來說
對于服務器端可以
①創建ServerSocket
②使用accept等待客戶端連接
③使用OutputStream發送數據
④使用InputStream接收數據
⑤在通信完成以后關閉輸入輸出流,關閉socket
對于客戶端可以
①創建Socket對象,連接服務器端
②使用InputStream接收數據
③使用OutputStream發送數據
④在通信完成以后關閉輸入輸出流,關閉socket
二.實現點對點對聊
1.簡單的接收數據
在進行較為復雜的操作之前,我們不妨先進行一些簡單的接收數據的操作。比如下面
服務器端
import java.io.*;
import java.net.*;
//創建一個類Server管理服務器端的內容
//1.服務器端的程序運行在服務器上,所以IP地址默認是當前電腦的IP地址
class Server{
private int port;//端口號
private ServerSocket serverSocket;
public Server(int port) {
this.port = port;
}
public void start() throws IOException {
//創建提供服務的socket
serverSocket = new ServerSocket(port);
//等待用戶連接accept
//如果有客戶端來連接,就得到這個客戶端的socket對象
//如果沒有客戶端來連接,就阻塞(一直等待)
Socket socket = serverSocket.accept();
//有了連接,就可以向客戶端發送響應信息
//輸出流如果輸出完畢,必須關閉,表示輸出結束
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("連接成功,可以通信了!");
socket.shutdownOutput();//關閉輸出流
//接收客戶端的響應信息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
//讀取數據
System.out.println(br.readLine());
}
}
public class Myclass {
public static void main(String[] args) throws IOException {
Server server = new Server(8888);
server.start();
}
}
客戶端
import java.io.*;
import java.net.*;
//創建一個類Client管理客戶端的內容
class Client{
private int port;//客戶端的端口號
private String ipAddr;//客戶端的IP地址
private Socket socket;
public Client(int port,String ipAddr) {
this.port = port;
this.ipAddr = ipAddr;
}
public void start() throws UnknownHostException, IOException {
//向服務器端發起連接
socket = new Socket(ipAddr,port);
//接收服務器端的響應信息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
//讀取數據
System.out.println(br.readLine());
//向服務器端發送數據
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
bw.write("你好!");
bw.flush();//注意,要flush
//關閉輸出流
socket.shutdownOutput();
}
}
public class Myclass {
public static void main(String[] args) throws UnknownHostException, IOException {
Client client = new Client(8888,"127.0.0.1");
client.start();
}
}
以上程序實現了服務器端的簡單連接和數據的簡單發送。下面,讓程序稍微復雜一點
服務器端
//服務器端不斷輸入
Scanner scanner = new Scanner(System.in);
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
while(scanner.hasNext()) {
ps.println(scanner.nextLine());
}
客戶端
//接收服務器端的響應信息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
//讀取數據(不斷接收)
String line = null;
while((line = br.readLine()) != null) {
System.out.println(line);
}
這樣,在服務器端發送數據,客戶端就可以接收到并且打印出來,這樣就實現了點對點的對聊
注意:readLine()這個方法會阻塞,也就是說它會一直等待內容,沒有內容就一直等著。包括scanner的nextLine()也是,一直等著你輸入,也會阻塞。
三.實現群聊
1.引
上面實現的點對點對聊只能是單方向的,不能既發送數據又接收數據,因為只有一個主線程,程序執行是從上到下的。所以若想真正實現兩者之間的”對話“,就必須使用多線程。一個線程是接收數據的,一個線程是發送數據的。不論是客戶端還是服務器端,都是如此。這里就不再寫,之間實現群聊功能。
2.介紹
做群聊的時候,服務器起的作用就是 數據的緩存和分發。當一個客戶端發送數據給服務器端之后,服務器端要把這個數據向所有的(除了發送此數據的客戶端)客戶端發送這個數據,從而造成的結果是一個人在群里面發送消息,群里面所有人都能接收到。
3.注意點
①一定要在receive接收之后再分發。
②要把每個客戶端的socket保存在數組中
4.具體代碼
服務器端
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Scanner;
//創建一個類Server管理服務器端的內容
//1.服務器端的程序運行在服務器上,所以IP地址默認是當前電腦的IP地址
class Server{
private int port;
private ServerSocket serverSocket;
//private Socket socket;
private ArrayList<Socket> sockets;
public Server(int port) {
this.port = port;
sockets = new ArrayList<>();
}
public void start() {
//創建服務的socket對象
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
//等待連接
while(true) {
try {
//接收客戶端的連接
Socket socket = serverSocket.accept();
//保存這個客戶端對應的socket對象
sockets.add(socket);
//創建一個線程用于發送數據
new Send(socket).start();
//創建一個線程用于接收數據
new Receive(socket,sockets).start();
} catch (Exception e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
}
}
//發送線程
class Send extends Thread{
private Socket socket;
public Send(Socket socket) {
this.socket = socket;
}
public void run() {
Scanner scanner = null;
PrintStream ps = null;
try {
scanner = new Scanner(System.in);
ps = new PrintStream(socket.getOutputStream());
while(scanner.hasNext()) {
ps.println(scanner.nextLine());
}
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}finally{
scanner.close();
ps.close();
}
}
}
//接收線程
class Receive extends Thread{
private Socket socket;
private ArrayList<Socket> lists;
public Receive(Socket socket,ArrayList<Socket> lists) {
this.socket = socket;
this.lists = lists;
}
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while((line = br.readLine()) != null) {
//分發內容
for(Socket s:lists) {
//判斷是不是當前這個客戶端
if(s != socket) {
PrintStream ps = new PrintStream(s.getOutputStream());
ps.println(line);
}
}
}
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}finally {
try {
br.close();
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
}
}
public class Myclass {
public static void main(String[] args) throws IOException {
Server server = new Server(8888);
server.start();
}
}
客戶端(客戶端可以有多個,但是代碼都可以是一樣的,所以我在這里就寫出一個)
import java.io.*;
import java.net.*;
import java.util.Scanner;
//創建一個類Client管理客戶端的內容
class Client{
private int port;
private String ip;
private Socket socket;
public Client(int port,String ip) {
this.port = port;
this.ip = ip;
}
public void start() {
//連接服務器端
try {
socket = new Socket(ip,port);
}catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
//開啟線程發送數據
new Send(socket).start();
//開啟線程接收數據
new Receive(socket).start();
}
}
class Send extends Thread{
private Socket socket;
public Send(Socket socket) {
this.socket = socket;
}
public void run() {
Scanner scanner = null;
PrintStream ps = null;
try {
scanner = new Scanner(System.in);
ps = new PrintStream(socket.getOutputStream());
while(scanner.hasNext()) {
ps.println(scanner.nextLine());
}
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}finally {
scanner.close();
ps.close();
}
}
}
class Receive extends Thread{
private Socket socket;
public Receive(Socket socket) {
this.socket = socket;
}
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
br.close();
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
}
}
public class Myclass {
public static void main(String[] args) throws UnknownHostException, IOException {
Client client = new Client(8888,"127.0.0.1");
client.start();
}
}
總結
一開始socket沒有搞懂,后來通過查閱很多資料就大概了解是什么意思了。在網絡編程的這些代碼中,很多代碼思路都是一樣的,比如客戶端和服務器端的代碼就有很多相同點和類似的地方。比如都要有端口號呀,然后都有接收和發送數據等等。到今天為止網絡編程學習結束,但是我自知自己還未完全掌握,甚至連百分之五十都可能沒掌握到,沒真正學會,所以還是得抽時間復習網絡編程的這些內容,包括之前Java的部分內容。