以下為本人的學習筆記 1.網路編程基本概念 1.1 什麼是電腦網路 把發佈在不同地理區域的電腦與專門的外部設備用通信線路互連成一個規模大、功能強的網路系統,從而使眾多的電腦可以方便地互相傳遞消息,共用硬體、軟體、數據消息等資源 1.2 電腦網路的主要功能 資源共用 信息傳輸與集中處理 均衡負 ...
以下為本人的學習筆記
1.網路編程基本概念
1.1 什麼是電腦網路
把發佈在不同地理區域的電腦與專門的外部設備用通信線路互連成一個規模大、功能強的網路系統,從而使眾多的電腦可以方便地互相傳遞消息,共用硬體、軟體、數據消息等資源
1.2 電腦網路的主要功能
-
資源共用
-
信息傳輸與集中處理
-
均衡負荷與分佈處理
-
綜合信息服務(www/綜合業務數字網路ISDN等)
1.3網路通信協議
要使電腦連成的網路能夠互通信息,需要對數據傳輸速率、傳輸代碼、代碼結構、傳輸控制步驟、出錯控制等制定一組標準,這一組共同遵守的通信標準就是網路通信協議,不同的電腦之間必須使用相同的通訊協議才能進行通信
網路通信介面
為了使兩個節點之間能夠進行對話,必須在他們之間建立通信工具(即介面),使彼此之間能進行信息交換。介面包括兩部分:
1)硬體裝置:實現節點之間的信息傳送(網線....)
2)軟體裝置:規定雙方進行的約定協議
1.4 TCP/IP
TCP/IP;參數控制協議/網際網路協議,又叫網路通訊協議,這個協議是Internet最基本的協議、Internet國際互聯網路的基礎,簡單來說,就是由網路層的IP協議和傳輸層的TCP協議組成的。
IP地址:網路中每台電腦的一個標識號,本地IP:127.0.0.1 或者 localhost
埠號(PORT):埠號的範圍:0-65535之間,0-1023之間的埠數是用於一些知名的網路服務和應用,通過埠號找到具體要通信的軟體
1.5程式開髮結構
網路編程主要是指完成C/S程式的開發,程式的開髮結構有兩種:
-
C/S(客戶端/伺服器)
開發兩套程式,兩套程式需要同時維護,例如:QQ。CS程式一般比較穩定
-
B/S(瀏覽器/伺服器)
開發一套程式,客戶端使用瀏覽器進行訪問,例如:各個論壇。BS程式一般穩定性較差,而且安全性較差。
但是,C/S的程式開發在實際的java應用中很少,而且整個java基本上都是以B/S為主
C/S程式主要可以完成以下兩種程式的開發:
-
TCP:(Transmission Control Protocol) 傳輸控制協議,採用三方握手的方式,保證準確的連接操作
-
UDP:(User Datagram Protocol) 數據報協議,發送數據報,例如:手機簡訊或者是QQ消息
TCP、UDP的數據幀格式簡單圖例:
其中協議類型用於區分TCP、UDP
2.網路編程TCP協議
2.1TCP程式概述
TCP是一個可靠的協議,面向連接的協議
實現TCP程式,需要編寫伺服器端和客戶端,Java API為我們提供了java.net包,為實現網路應用程式提供類
ServerSocket:此類實現伺服器套接字
Socket:此類實現客戶端套接字(也可以就叫“套接字”)
硬體都有驅動,網路驅動層指的是網卡的驅動
通過Socket來實現網路編程,Socket是網路驅動層提供給應用程式編程的介面和一種訪問機制,
解釋:數據寫到Socket中,Socket就會把數據對接到驅動層,驅動層把數據對接到網卡,再通過網線傳出去
也可以將Socket想象成一個快遞員
2.2 數據發送過程
2.3 數據接收過程
2.4 實現伺服器端與客戶端程式
伺服器端:
public class ServerSocket extends Object implements Closeable
這個類實現了伺服器套接字。 伺服器套接字等待通過網路進入的請求。 它根據該請求執行一些操作,然後可能將結果返回給請求者。
method | 說明 |
---|---|
ServerSocket(int port) |
創建綁定到指定埠的伺服器套接字。 |
setSoTimeout(int timeout) |
啟用/禁用 SO_TIMEOUT 帶有指定超時,以毫秒為單位。 |
getInetAddress() |
返回此伺服器套接字的本地地址。 |
accept() |
偵聽要連接到此套接字(socket)並接收它。 |
客戶端:
public class Socket extends Object implements Closeable
該類實現客戶端套接字(也稱為“套接字”)。 套接字是兩台機器之間通訊的端點。
method | 說明 |
---|---|
Socket(InetAddress address, int port) |
創建流套接字並將其連接到指定IP地址的指定埠號 |
getInputStream() |
返回此套接字的輸入流。 |
getOutputStream() |
返回此套接字的輸出流。 |
setSoTimeout(int timeout) |
啟用/禁用指定超時的 SO_TIMEOUT (以毫秒為單位)。 |
3.TCP實現ECHO程式
Echo,意為應答,程式的功能是客戶端向伺服器發送一個字元串,伺服器不做任何處理,直接把字元串返回給客戶端,Echo程式是最為基本的客戶/伺服器程式。(相當於java的HelloWorld)
public class EchoServerDeom{
public static void main(String[] args){
//1.創建一個伺服器端的Socket(1024-65535的埠號)
try{
ServerSocket server = new ServerSocket(port:6666)
System.out.println("伺服器已啟動,正在等待客戶端的連接...");
//2.等待客戶端的連接,造成阻塞,如果有客戶端連接成功,立即返回一個Socket對象
Socket socket = server.accpet();
System.out.println("客戶端連接成功:"+server.getInetAddress().getHostAddress());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream());
//通過輸入流讀取網路數據,如果沒有數據,那麼會阻塞
String info = br.readLine();
System.out.println(info);
//獲取輸出流,向客戶端返回消息
PrintStream ps = new PrintStream (new BufferedOutputStream(socket.getOutputStream()));
ps.println("echo"+info);
ps.flush();
//關閉
ps.close();
br.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
public class EchoClientDeom{
public static void main(String[] args){
//1.創建一個Socket對象,指定要連接的伺服器
try{
Socket socket = new Socket("localhost",6666);//主機名+埠號
//獲取socket的輸入輸出流
PrintStream ps = new PrintStream(new BufferedOutputStream(socket.getOutPutStream()));
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
ps.println("hello,my name is bin");
ps.flush();
//讀取伺服器端返回的數據
String info = br.readLine();
System.out.println(info);
ps.close();
br.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
兩個對象創建成功後面就是流的操作
4.伺服器與多客戶端通信
要想伺服器同時支持多個客戶端的連接,就必須加入多線程的處理機制,將每一個連接的客戶端都創建一個新的線程對象(用迴圈)
伺服器端通過加入線程池來處理多個客戶端請求,簡單的設置線程數可以與CPU核數匹配,過多的線程數空閑會消耗伺服器為資源
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//伺服器,用迴圈處理多個客戶端
public class ServerSocketDemo2 {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(6666);
System.out.println("伺服器已啟動,等待客戶端連接...");
ExecutorService es = Executors.newFixedThreadPool(3);
while (true){
Socket socket = server.accept();
System.out.println(socket.getInetAddress().getHostAddress());
es.execute(new UserThread(socket));//一個線程處理一個客戶端,將一個客戶端socket作為線程的一個參數
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//用來處理客戶端請求的線程
class UserThread implements Runnable{
private Socket socket;
UserThread(Socket socket){
this.socket = socket;
}
public void run(){
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
String info = br.readLine();
System.out.println(info);
pw.println("echo:"+info);
pw.flush();
br.close();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
//客戶端
public class MutilClientDemo {
public static void main(String[] args) {
try {
Scanner input = new Scanner(System.in);
//創建一個socket對象,指定要連接的伺服器
Socket socket = new Socket("localhost", 6666);
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintStream ps = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
System.out.println("請輸入一段話:");
String info = input.nextLine();
ps.println(info);
ps.flush();
//讀取伺服器返回的數據
info = br.readLine();
System.out.println(info);
br.close();
ps.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5.多客戶端之間的通信
伺服器可以與多個客戶端實現通信了,那我們真正的目的就是要實現多個客戶端之間的通信,使用TCP協議實現的方案是:客戶端的數據包通過伺服器中轉,發送到另一個客戶端,如下圖所示:
多客戶端間通信案例:
伺服器通過消息類型來判斷發送消息的客戶端是什麼意圖,消息類型是我們自定義的標記
圖解分析:
伺服器端:
/*伺服器作為一個中轉,轉發消息給多個客戶端之間*/
public class Server {
public static void main(String[] args) {
//2.創建集合,保存客戶端處理的線程,用來找其他指定線程
Vector<UserThread> vector = new Vector<UserThread>();
//3.創建線程池
ExecutorService es = Executors.newFixedThreadPool(5);
//1.創建伺服器端的Socket
try {
ServerSocket server = new ServerSocket(7777);
System.out.println("伺服器已啟動,正在等待連接...");
while (true){
//4.接收一個客戶端的socket
Socket socket = server.accept();
UserThread user = new UserThread(socket,vector);
//5.線程執行一個客戶端
es.execute(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//客戶端處理的線程
class UserThread implements Runnable{
private String name ;//客戶端的用戶名稱(唯一)
private Socket socket;
private Vector<UserThread> vector;//客戶端處理線程的集合
private ObjectInputStream ois;
private ObjectOutputStream oos;
private boolean flag = true;
public UserThread(Socket socket, Vector<UserThread> vector) {
this.socket = socket;
this.vector = vector;
vector.add(this);
}
@Override
public void run() {
try {
//6.輸出客戶端socket主機地址
System.out.println("客戶端"+socket.getInetAddress().getHostAddress());
//7.構建序列化輸入輸出對象流,
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
while (flag){
//8.讀取消息對象
Message msg = (Message)ois.readObject();
//9.判斷消息類型
int type = msg.getType();
switch (type){
//發送消息的類型
case MessageType.TYPE_SEND:
String to = msg.getTo();
UserThread ut ;
int size = vector.size();
//遍歷線程集合里的線程名字,符合就把消息writ給該線程
for (int i = 0; i < size; i++) {
ut = vector.get(i);//取一個變數裝這個線程
if (to.equals(ut.name) && ut != this){
ut.oos.writeObject(msg);//把該消息發給該線程
break;
}
}
break;
//登錄消息類型
case MessageType.TYPE_LOGIN:
//獲取發送者名字
name = msg.getFrom();
msg.setInfo("歡迎你:");
//輸出客戶端歡迎數據
oos.writeObject(msg);
break;
}
}
//10.關閉流
ois.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
客戶端:
public class Client {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
ExecutorService es = Executors.newSingleThreadExecutor();
try {
//1.創建一個客戶端的socket
Socket socket = new Socket("localhost",7777);
System.out.println("伺服器連接成功...");
//2.構建序列化輸入輸出對象流
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
//3.向伺服器發送登錄消息
System.out.println("請輸入名稱:");
String name = input.nextLine();
Message msg = new Message(name,null,MessageType.TYPE_LOGIN,null);
oos.writeObject(msg);
msg = (Message)ois.readObject();
System.out.println(msg.getInfo()+msg.getFrom());
//啟動(不斷)讀取消息的線程
es.execute(new ReadInfoThread(ois));
//使用主線程來實現發送消息
boolean flag = true;
while(flag){
msg = new Message();
System.out.println("To:");
msg.setTo(input.nextLine());
msg.setFrom(name);
msg.setType(MessageType.TYPE_SEND);
System.out.println("Info:");
msg.setInfo(input.nextLine());
oos.writeObject(msg);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//讀取消息
class ReadInfoThread implements Runnable{
private ObjectInputStream in;
private boolean flag = true;
public ReadInfoThread(ObjectInputStream in) {
this.in = in;
}
public void setFlag(boolean flag){
this.flag= flag;
}
@Override
public void run() {
try {
//不斷地讀取消息
while (flag){
Message message = (Message)in.readObject();
System.out.println("["+message.getFrom()+"]對我說:"+message.getInfo());
}
if (in!=null){
in.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
import java.io.Serializable;
/*消息包*/
public class Message implements Serializable {//序列化對象實現Serializable介面
private String from;//發送者
private String to;//接收者
private int type;//消息類型
private String info;//消息
public Message() {
}
public Message(String from, String to, int type, String info) {
this.from = from;
this.to = to;
this.type = type;
this.info = info;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Message{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", type=" + type +
", info='" + info + '\'' +
'}';
}
}
public final class MessageType {
public static final int TYPE_LOGIN = 0x1;//登錄消息類型
public static final int TYPE_SEND = 0x2;//發送消息的類型
}
6.網路編程UDP協議
1.UDP協議概述
UCP是User Datagram Protocol的簡稱,是一種無連接的協議,每個數據報都是一個獨立的信息,包括完整的源地址或目的地址,它在網路上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的,每個被傳輸的數據報必須限定在64KB之內。
主要使用以下兩個類:
DatagramPacket:此類表示數據報包
DatagramSocket:此類表示用來發送和接收數據報包的套接字
UDP是無連接的,減少了開銷和發送數據之前的延遲,不保證可靠,UDP的報頭長度要小於TCP的包頭長度,例如QQ文件傳輸,pplive等都是使用UDP協議
伺服器端:
public class UDPServerDeom{
public static void main(String[] args){
String info = "good good 天天";
byte[] bytes = info.getBytes();
try{
DatagramPacket dp = new DatagramPacket(bytes,/**數據報包數據*/
0,/**0表示第0個位置,分組數據偏移量*/
bytes.length,/**數組長度*/
InetAddress.getByName("127.0.0.1"),/**目的地址*/
8000/**目的埠號*/);
//本程式的埠號
DatagramSocket socket = new DatagramSocket(9000);
socket.send(dp);
} catch(UnknownHostException e){
e.printStackTrace();
} catch( IOException e){
e.printStackTrace();
}
}
}
客戶端:客戶端要先啟動,在receive()阻塞,等待server發數據報
public class UDPClientDeom{
public static void main(String[] args){
byte[] bytes = new byte[1024];
//封裝成數據報包
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
try{
DatagramSocket socket = new DatagramSocket(8000);
System.out.println("正在接收數據中...");
socket.receive(dp);//接收數據,會造成阻塞
String s = new String(dp.getData(),0,dp.getLength());//從data中取,從0 到dp.getLength()
System.out.println(s);
socket.close();
} catch(UnknownHostException e){
e.printStackTrace();
} catch( IOException e){
e.printStackTrace();
}
}
}
7.URL
URL概述
URL(uniform resource location)類URL代表一個統一資源定位符,是互聯網上標準資源的地址,它是指向互聯網“資源”的指針,互聯網的每個文件都有唯一的一個URL。抽象類URLConnection是所有類的超類,它代表應用程式和URL之間的通信鏈接
public class URLDeom{
public static void main(String[] args){
try{
URL url = new URL("https://pic.cnblogs.com/avatar/2988753/20221012224248.png");//資源路徑
HttpURLConnection conn = (HttpURLConnection)url.openConnection();//打開鏈接,因為圖片地址是http協議,可以用HttpURLConnection
BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("D:\\xx\\wukong.jpg"));
byte[] bytes = new byte[1024];
int len = -1 ;
while((len = in.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
in.close();
out.close();
System.out.println("下載成功... ");
} catch(MalformedURLException e){
e.printStackTrace();
} catch( IOException e){
e.printStackTrace();
}
}
}
8.MINA框架
-
什麼是MINA?一句話就是:一個簡潔易用的基於TCP/IP通信的java框架
-
下載地址:http://mina.apache.org/downloads-mina.html 下載zip包
-
一個簡單的網路程式需要的最少jar包:mina-core-2.0.16.jar、slf4j-api-1.7.21.jar
-
開發一個MINA應用,簡單的說,就是①創建連接,②設定過濾規則,③編寫自己的消息處理器
-
Mina框架可以幫助我們快速開發高性能、高擴展的網路通信應用,Mian提供了事件驅動、非同步操作的編程模型,預設使用NIO作為底層支持
示例:
public class Server{
public static void main(String[] args){
//創建一個非阻塞的Server端Socket,用NIO
SocketAcceptor acceptor= new NioSocketAcceptor();//創建接收數據的過濾器
DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();//chain鏈
//設定這個過濾器將一行一行(/r/n)的讀取數據
chain.addLast("myChin",new ProtocolCodecFilter(new TextLineCodecFactory()));
//如,設定伺服器端的消息處理器:一個MainaServerHandler對象
acceptor.setHandler(new MinaServerHandler());
int bindPort = 9999;//伺服器埠號
try{
//綁定埠,啟動伺服器
//立即返回
acceptor.bind(new InetSocketAddress(bindPort));
}catch(IOException e){
e.printStackTrace();
}
System.out.println("Mina Server is running: = "+bindPort);
}
}
/**
伺服器端的消息處理器
*/
public class MinaServerHandler extends IoHandleAdapter{
//一次會話被打開
public void sessionOpened(IoSession session) throws Exception{
super.sessionOpened(session);
System.out.println("welcome client"+session.getRemoteAddress());
}
//會話被關閉
public void sessionClosed(IoSession session) throws Exception{
super.sessionClosed(session);
System.out.println("client closed");
}
//接收消息
public void messageReceived(IoSession session,Object message) throws Exception{
super.messageReceived(session,message);
String msg = (String)message;//接收到的消息對象
System.out.println("收到客戶端發來的消息: "+msg);
//向客戶端發送消息對象
session.write("echo:"+msg);
}
}
}
使用telnet測試:telnet localhost 9999
public class Client{
public static void main(String[] args){
NioSocketConnector connector = new NioSocketConnector();//創建接收數據的過濾器
DefaultIoFilterChainBuilder chain = connector.getFilterChain();
//設定這個過濾器將一行一行(/r/n)的讀取數據
chain.addLast("myChin",new ProtocolCodecFilter(new TextLineCodecFactory()));
//如,設定伺服器端的消息處理器:一個SamplMainaServerHandler對象
connector.setHandler(new MinaServerHandler());
connector.setConnectTimeoutMillis(30);//Set connect timeout.
//連接到伺服器:
ConnectFuture cf = connector.connect(new InsetSocketAddress("localhost",9999));
//等待連接成功,立即返回
cf.awaitUninterruptibley();
Scanner input = new Scanner(System.in);
while(true){
System.out.println("請輸入:");
String info = input.nextLine();
//發送消息
cf.getSession().write(info);
}
//等待伺服器連接關閉,結束長連接
//cf.getSession().getCloseFuture().awaitUninterruptibly();//會阻塞
//connector.dispose(); //釋放
}
}
public class SampleMinaServerHandler extends IoHandleAdapter{
//當一個客戶端連接進入時
public void sessionOpened(IoSession session) throws Exception{
System.out.println("incomming client"+session.getRemoteAddress());
super.sessionOpened(session);
session.writ("我來啦");
}
//當一個客戶端關閉時
public void sessionClosed(IoSession session) {
super.sessionClosed(session);
System.out.println("client closed");
}
//當一個客戶端發送的消息到達時:
public void messageReceived(IoSession session,Object message) throws Exception{
super.messageReceived(session,message);
//我們已設定了伺服器解析消息的規則是一行一行讀取,這裡就可轉為String;
String msg = (String)message;
System.out.println("收到伺服器端發來的消息: "+msg);
//測試將消息會送給客戶端
session.write(msg);
}
}
使用Mina直接傳送對象
1.public class User info implements java.io.Serializable
2.伺服器,客戶端都設定以對象為單位
//設定這個過濾器將以對象為單位讀取數據
ProtocolCodecFilter filter = new ProtocolCodecFilter(new ObjectSerializationCodecFactory());
chain.addLast("objectFilter",filter);
3.接收對象
public void messageReceived(IoSession session,Object message) throws Exception{
//我們已設定了伺服器解析消息的規則一個Userinfo對象為單位傳輸:
Userinfo us = (Userinfo)message;
}
public class Message implements Serializable{
private String from ;
private String to;
private String type;
private String info;
......
}
■免責申明
⒈ 本站是純粹個人學習網站,與朋友交流共賞,不存在任何商業目的。
⒉ 本站利用了部分網路資源,版權歸原作者及網站所有,如果您對本站所載文章及作品版權的歸屬存有異議,請立即通知我們,我們將在第一時間予以刪除,同時向你表示歉意!
2022-10-14 17:33:19