TCP、UDP編程練習
TCP
☆上傳文本文件
讀取一個本地文本文件,將數據發送到服務端,服務器端對數據進行存儲。 存儲完畢後,給客戶端一個提示。
一、解題思路
客戶端:(1) 創建Socket對象----用服務器的ip+端口號
(2)讀取文件內容
(3)通過socket把內容發送給服務器端(把socket中的輸出流包裝成“打印流”來進行發送文本,是一種比較安全的輸出方式,不會出現失真。)
服務器端:(1) 創建服務器socket---ServerSocket
(2)通過ServerSocket獲得客戶端的socket
(3)通過客戶端的socket,讀取對方發來的數據,並把這些數據寫到一個新建文件當中
二、註解和實現代碼
UploadTextClient.java類
package net.tcp.textFileUpload;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class UploadTextClient {
public static void main(String[] args) {
try {
//1 創建Socket對象----用服務器的ip+端口號
Socket s = new Socket("127.0.0.1", 10000);
//2 讀取文件內容 //3 通過socket把內容發送給服務器端(把socket中的輸出流包裝成“打印流”來進行發送文本,是一種比較安全的輸出方式,不會出現失真。)
BufferedReader bufr = new BufferedReader( new FileReader("tempFile\\client.txt") );
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
//發送
String line = null;
while((line=bufr.readLine())!=null){
out.println(line);
}
//6 給服務器發送一個結束標記
//out.println("%$#@88##K##");//發送自定義的結束標記
s.shutdownOutput();
//5 讀取服務器端的反饋信息 s.getInputStream()
BufferedReader bufr2 = new BufferedReader( new InputStreamReader(s.getInputStream()) );
String text = bufr2.readLine();
System.out.println("server:"+text);
//4 關流
bufr.close();
out.close();
s.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
UploadTextServer.java類
package net.tcp.textFileUpload;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadTextServer {
public static void main(String[] args) {
try {
//1 創建服務器socket---ServerSocket
ServerSocket server = new ServerSocket(10000);
//2 通過ServerSocket獲得客戶端的socket
Socket s = server.accept();
//3 通過客戶端的socket,讀取對方發來的數據,並把這些數據寫到一個新建文件當中
//源:socket-->s.getInputStream()-->BufferedReader 目的:FileWriter("tempFile\\server.txt")--> 可以包裝成PrintWriter來進行輸出
BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter pw = new PrintWriter( new FileWriter("tempFile\\server.txt") ,true);
String line = null;
while((line=bufr.readLine())!=null){
// if("%$#@88##K##".equals(line)){
// break;
// }
pw.println(line);
}
//5 向客戶端發送反饋信息,通過客戶端的socket來發
String str = s.getInetAddress().getHostName();
str = str + "::: 文件上傳成功...";
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println(str);
//4 關流
bufr.close();
pw.close();
s.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
☆上傳圖片文件
客戶端需求:把一個圖片文件發送到服務端並讀取回饋信息。要求判斷文件是否存在及格式是否爲jpg或gif並要求文件小於2M。
服務端需求:接收客戶端發送過來的圖片數據。進行存儲後,回饋一個 上傳成功字樣。支持多用戶的併發訪問。
一、解題思路
客戶端:(1)判斷文件輸出的文件是否存在:file.exists() && file.isFile()
(2)判斷文件的格式問題:file.getName().endsWith(".jpg")||file.getName().endsWith(".gif")
(3)文件小於2M:file.length()>=1024*1024*2
(4)然後通過IO讀出文件的數據,並通過Socket將其發送給服務器端
注:在文件發送完後,記得給一個發送結束標誌 s.shutdownOutput(),這句相當重要哦!!!
服務器端:主要就是解決多個客戶端同時向服務器傳送圖片時,服務器端如何處理的問題。方法是通過new一個線程來解決。只要有客戶端請求,就new一個線
程單獨的與其交流,這要做的好處,滿足了每個客戶端同時和一個服務器進行多次交流。
二、註解和實現代碼
UploadPicClient.java類
package net.tcp.picFileUpload;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class UploadPicClient {
public static void main(String[] args) throws Exception{
//衛條件,反邏輯
if(args.length!=1){
System.out.println("請指定文件名");
return;
}
File file = new File(args[0]);
if( !(file.exists() && file.isFile()) ){
System.out.println("上傳的文件不存在");
return;
}
if( !(file.getName().endsWith(".jpg")||file.getName().endsWith(".gif")) ){
System.out.println("文件的擴展名必須是jpg或gif!");
return;
}
if( file.length()>=1024*1024*2 ){
System.out.println("文件過大,必須小於2M,請重新選擇...");
return;
}
Socket s = new Socket("127.0.0.1",10002);
FileInputStream fis = new FileInputStream(file);
OutputStream out = s.getOutputStream();
byte buf[] = new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
out.write(buf, 0, len);
}
s.shutdownOutput();//發送結束標記
//讀取服務器的反饋信息
InputStream in = s.getInputStream();
byte b[] = new byte[1024];
int len2 = in.read(b);
String info = new String(b,0,len2);
System.out.println(info);
fis.close();
out.close();
s.close();
}
}
UploadPicServer.java類
package net.tcp.picFileUpload;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadPicServer {
public static void main(String[] args) throws Exception{
ServerSocket server = new ServerSocket(10002);
while(true){
Socket s = server.accept();
new Thread( new UploadThread(s) ).start();
}
}
}
class UploadThread implements Runnable{
private Socket s = null;
public UploadThread(Socket s) {
this.s = s;
}
@Override
public void run() {
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected!");
//接收來自客戶端的數據(圖片文件)
File dir= new File("c:\\mypic");
if(!dir.exists()){
dir.mkdir();
}
int count =1;
File file = new File(dir,ip+".jpg");
while(file.exists()){
file = new File(dir,ip+"("+ (count++) +").jpg");
}
try {
FileOutputStream fos = new FileOutputStream(file);
byte buf[] = new byte[1024];
int len=0;
InputStream in = s.getInputStream();
while((len=in.read(buf))!=-1){
fos.write(buf, 0, len);
}
OutputStream out = s.getOutputStream();
out.write((ip+"-->圖片數據已經上傳成功!").getBytes());
//關流
out.close();
fos.close();
s.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意一個小問題:就是在運行客戶端程序(UploadPicClient.java)時,不能直接運行。
操作步驟:鼠標放在main方法上單擊右鍵,選擇Run As ---> Run Configurations --->Arguments --->Program arguments中填寫要傳輸文件的絕對路徑,也就是路徑加文
件名。
在客戶端與服務器進行傳輸數據時,發送端和接收端的流一定要對應。
UDP
☆UDP聊天程序
a、通過鍵盤錄入獲取要發送的信息。
b、將發送和接收分別封裝到兩個線程中。
一、解題思路
因爲是聊天程序,所以是兩個客戶端交流,所以只要寫成一個客戶端就可以,另一個只需要改一下發送端和接收端的端口。
每個客戶端都有一個發送的DatagramSocket和一個接收的DatagramSocket;然後分別通過一個線程啓動。這樣保證在沒有接到結束提示時,每個客戶端都可以一直保持接收和發送的狀態。
二、註解和實現代碼
UDPChat.java類
package net.udp.updChat;
//111111
import java.net.DatagramSocket;
import java.net.SocketException;
public class UDPChat {
public static void main(String[] args) {
try {
DatagramSocket send = new DatagramSocket(10003);//發送端口
DatagramSocket receive = new DatagramSocket(10004);//接收端口
new Thread( new Send(send) ).start();
new Thread( new Receive(receive) ).start();
} catch (SocketException e) {
e.printStackTrace();
}
}
}
Receive.java類
package net.udp.updChat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Receive implements Runnable {
DatagramSocket ds=null;
public Receive(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
byte buf[] = new byte[1024];
while(true){
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String str = new String( dp.getData(),0,dp.getLength() );
System.out.println(ip+":"+port+"==> "+str);
if("over".equalsIgnoreCase(str)){
System.out.println(ip+"離開聊天室....");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Send.java類
package net.udp.updChat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Send implements Runnable {
DatagramSocket ds=null;
public Send(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line=null ;
while((line= bufr.readLine())!=null){
byte buf[] = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.31.115"),10002);//(10002)與另一個客戶端的接收端口相同
ds.send(dp);
if("over".equalsIgnoreCase(line)){
break;
}
}
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}