創建鏈接,造成傳輸數據的通道。
在鏈接中進行大數據量傳輸
經過三次握手完成鏈接,是可靠協議
必須創建鏈接,效率會稍低java
Socket 和
ServerSocket類編程
TCP Socket:IP地址和端口,套接字緩存
Socket和ServerSocket
創建客戶端和服務器端
創建鏈接後,經過Socket中的IO流進行數據的傳輸
關閉socket 服務器
一樣,客戶端與服務器端是兩個獨立的應用程序。markdown
☆基本思路(客戶端)網絡
客戶端須要明確服務器的ip地址以及端口,這樣才能夠去試着創建鏈接,若是鏈接失敗,會出現異常。
鏈接成功,說明客戶端與服務端創建了通道,那麼經過IO流就能夠進行數據的傳輸,而Socket對象已經提供了輸入流和輸出流對象,經過getInputStream(), getOutputStream()獲取便可。
與服務端通信結束後,關閉Socket。併發
☆基本思路(服務器端)socket
服務端須要明確它要處理的數據是從哪一個端口進入的。
當有客戶端訪問時,要明確是哪一個客戶端,可經過accept()獲取已鏈接的客戶端對象,並經過該對象與客戶端經過IO流進行數據傳輸。
當該客戶端訪問結束,關閉該客戶端。tcp
☆客戶端ide
經過Socket創建對象並指定要鏈接的服務端主機以及端口。
Socket s = new Socket(「192.168.1.1」,9999);
OutputStream out = s.getOutputStream();
out.write(「hello」.getBytes());
s.close();
☆服務器端
創建服務端須要監聽一個端口
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+」:」+str);
s.close();
ss.close();
客戶端代碼:
package cn.hncu.tcp;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
/** * 客戶端 * @author 陳浩翔 * * 2016-5-10 */
public class MyClientSocket {
public static void main(String[] args) {
try {
//由於是在本身本機上演示,IP就直接填寫本機10.30.7.95的了。
//這個端口和IP都是服務器端的(本身能夠改的)
Socket s = new Socket("10.30.7.95", 9999);
//和服務器進行三次握手,若失敗則出異常,不然返回和對方通信的socket
OutputStream os = s.getOutputStream();
//發送數據
os.write("你好,服務器!".getBytes());
//接收服務器端的反饋
InputStream in = s.getInputStream();
DataInputStream din = new DataInputStream(in);
System.out.println(din.readUTF());
s.close();
din.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服務器端代碼:
package cn.hncu.tcp;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/** * 服務器端 * @author 陳浩翔 * * 2016-5-10 */
public class MyServerSocket {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(9999);
while(true){
System.out.println("準備接收一個數據...");
Socket s = server.accept();//阻塞式方法
System.out.println("接收了一個數據...");
//讀--從客戶端讀數據
InputStream in = s.getInputStream();
byte buf[] = new byte[1024];
in.read(buf);
System.out.println("read info: "+new String(buf));
//寫--應答客戶端--向他寫數據
OutputStream out = s.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
dout.writeUTF("你好,"+s.getInetAddress().getHostAddress()+" ,你的信息已收到。");
dout.close();
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客戶端運行結果:
你好,10.30.7.95 ,你的信息已收到。
服務器端運行結果:
☆思考
對於Web服務器而言,當有多個客戶端同時訪問服務器時,服務端又如何提供服務呢?
☆TCP傳輸最容易出現的問題
客戶端鏈接上服務端,兩端都在等待,沒有任何數據傳輸。
經過例程分析:
由於read方法或者readLine方法是阻塞式。
解決辦法:
自定義結束標記(必須定義傳輸文件中沒有這個這個字符串的,否則會出現接收數據不完整)
使用shutdownInput,shutdownOutput方法。
讀取一個本地文本文件,將數據發送到服務端,服務器端對數據進行存儲。 存儲完畢後,給客戶端一個提示。
package cn.hncu.tcp.upload;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/** * 文本文件發送的客戶端 * @author 陳浩翔 * * 2016-5-10 */
public class UploadTextClient {
public static void main(String[] args) {
try {
Socket s = new Socket("", 10006);
//思路:把本地文件的數據讀取出來經過 s.getOutputStream()得到的out對象發送出去
BufferedReader bf = new BufferedReader(new FileReader("tempfiles\\client.txt"));
OutputStream out = s.getOutputStream();//這裏的輸出流 對應的是服務器端的輸入流
PrintWriter pw = new PrintWriter(out,true);//建議不要用BufferedWriter
//!!!!!!!!!!!!!!!!!這個true不要忘了!---自動刷新
//如今你們寫網絡傳輸文件,通常是用PrintWriter
String str=null;
while((str=bf.readLine())!=null){
pw.println(str);
}
//給服務器發送結束標記---上傳結束,要加結束標記,
//不然服務器在數據接收完畢時再調用read()或readLine()時會出異常
//法1:pw.println("over#$@#@$");//不能出現文件中存在的結束關鍵字---搞特殊一點
//法2---建議採用該種方式---由socket內部來指定結束標記
s.shutdownOutput();
bf.close();
//接收服務器端反饋
InputStream in = s.getInputStream();
DataInputStream din = new DataInputStream(in);
System.out.println("server應答:"+din.readUTF());
s.close();
din.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package cn.hncu.tcp.upload;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/** * 文本文件接收的服務器端 * @author 陳浩翔 * * 2016-5-10 */
public class UploadTextServer {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(10006);
Socket s = server.accept();
System.out.println(s.getInetAddress().getHostAddress()+"...發送消息來");
//讀取客戶端上傳過來的文本文件
//源 ---socket(字節流)---額外:須要轉換成字符流 ,緩存流
InputStream in = s.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
//目的 ---硬盤字符流 FileWriter---額外:打印流
PrintWriter pw = new PrintWriter(new FileWriter("tempfiles\\server.txt"),true);
String line = null;
while((line=br.readLine())!=null){
// if("over#$@#@$".equals(line)){//本身定義的結束標誌
// break;
// }
pw.println(line);
}
pw.close();
//上傳成功,給客戶端一個提示信息
OutputStream out = s.getOutputStream();
DataOutputStream dout = new DataOutputStream(out);
dout.writeUTF("文件上傳成功!");
s.close();
server.close();
dout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
測試經過!
客戶端需求:把一個圖片文件發送到服務端並讀取回饋信息。要求判斷文件是否存在及格式是否爲jpg或gif並要求文件小於2M。
服務端需求:接收客戶端發送過來的圖片數據。進行存儲後,回饋一個 上傳成功字樣。支持多用戶的併發訪問。
package cn.hncu.tcp.upload;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import javax.swing.JOptionPane;
/** * 圖片文件的發送-客戶端 * @author 陳浩翔 * * 2016-5-10 */
public class UploadPicClient {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("請輸入圖片路徑:");
String str = sc.next();
File file = new File(str);
if(!(file.exists()&& file.isFile())){
JOptionPane.showMessageDialog(null, "文件不存在!");
return ;
}
if(!(file.getName().endsWith(".jpg")||file.getName().endsWith(".gif"))){
JOptionPane.showMessageDialog(null, "文件格式不對,文件擴展名必須是jpg或gif!");
return ;
}
if( file.length()>=1024*1024*2){
JOptionPane.showMessageDialog(null, "文件過大,不該超過2M,請從新上傳!");
return;
}
//上傳
try {
Socket s = new Socket("10.30.7.95", 10007);
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(str));
OutputStream out = s.getOutputStream();
byte buf[] = new byte[1024];
int len=0;
while((len=bin.read(buf))!=-1){
out.write(buf, 0, len);
}
s.shutdownOutput();//告訴服務器,文件上傳完畢
//讀取服務器的回饋信息
InputStream in = s.getInputStream();
byte buf2[] = new byte[1024];
int len2 = in.read(buf2);
System.out.println(new String(buf2, 0, len2));
//關流
out.close();
bin.close();
s.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package cn.hncu.tcp.upload;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/** * 圖片文件接收-服務器端 * @author 陳浩翔 * * 2016-5-10 */
public class UploadPicServer {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(10007);
while(true){
Socket s = server.accept();//阻塞方法
//只負責和客戶端進行握手
new Thread(new UploadThread(s) ).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class UploadThread implements Runnable{
private Socket s;
public UploadThread(Socket s) {
this.s = s;
}
@Override
public void run() {
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...發來圖片");
try {
BufferedInputStream bin = new BufferedInputStream(s.getInputStream());
File dir = new File("g:\\mypic");
if(!dir.exists()){
dir.mkdir();//文件夾不存在,建立mypic文件夾
}
int count=1;
//我以爲這裏的後綴名,須要經過發送方也發過來的
File file = new File(dir, ip+".jpg");
while(file.exists()){
file = new File(dir,ip+"("+(count++) +")"+".jpg"); //帶號的文件名
}
FileOutputStream fout = new FileOutputStream(file);
//從socket流中讀取數據,存儲到本地文件。至關於對拷
byte buf[] = new byte[1024];
int len=0;
while( (len=bin.read(buf))!=-1){
fout.write(buf, 0, len);
}
//圖片接收完畢
//向客戶端發送回饋信息
OutputStream out = s.getOutputStream();
out.write( "上傳成功".getBytes() );
fout.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
有一個小bug。是服務端接收的,由於我把後綴名統一爲jpg了,gif的圖片能夠上傳,只是變成靜圖了(jpg)。能夠經過改後綴名,再把這圖片改回去動圖。還有一種方法,在上傳的時候,把後綴名也上傳,再經過服務器解析就能夠解決這個問題。
由於時間問題,我就不寫了,這個很簡單的。
只是多發送了一個後綴名過去而已。
我仍是把那個後綴名的解決辦法寫了下:
在客戶端:修改的代碼以下:
//上傳
try {
Socket s = new Socket("10.30.7.95", 10007);
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(str));
//上傳文件後綴###########增長的
OutputStream out = s.getOutputStream();
String fileName = file.getName();
out.write(fileName.substring(fileName.length()-4, fileName.length()).getBytes());
byte buf[] = new byte[1024];
int len=0;
while((len=bin.read(buf))!=-1){
out.write(buf, 0, len);
}
s.shutdownOutput();//告訴服務器,文件上傳完畢
//讀取服務器的回饋信息
InputStream in = s.getInputStream();
byte buf2[] = new byte[1024];
int len2 = in.read(buf2);
System.out.println(new String(buf2, 0, len2));
//關流
out.close();
bin.close();
s.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
服務器端:
@Override
public void run() {
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...發來圖片");
try {
BufferedInputStream bin = new BufferedInputStream(s.getInputStream());
File dir = new File("g:\\mypic");
if(!dir.exists()){
dir.mkdir();//文件夾不存在,建立mypic文件夾
}
//讀取上傳過來的圖片後綴是什麼!#########
char cbuf[] = new char[4];
InputStreamReader insr = new InputStreamReader(bin);
insr.read(cbuf);
String str = new String(cbuf);
int count=1;
//我以爲這裏的後綴名,須要經過發送方也發過來的
File file = new File(dir, ip+str);
while(file.exists()){
file = new File(dir,ip+"("+(count++) +")"+str); //帶號的文件名
}
FileOutputStream fout = new FileOutputStream(file);
//從socket流中讀取數據,存儲到本地文件。至關於對拷
byte buf[] = new byte[1024];
int len=0;
//#########必須有這一句
bin.read(buf, 0, 8);
while( (len=bin.read(buf))!=-1){
fout.write(buf, 0, len);
}
//圖片接收完畢
//向客戶端發送回饋信息
OutputStream out = s.getOutputStream();
out.write( "上傳成功".getBytes() );
fout.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
好了,基本上就是這樣的,若是想要更好,作個圖形界面就好看了。本身動下手吧~~