實驗2《Windows Socket編程》
一、實驗目的
通過實驗,使學生熟悉並掌握計算機Windows 編程的基本知識,進一步加深學生對課堂所學基本內容的理解,掌握基本的Windows編程技巧,通過實驗使得學生能夠進行一些簡單的網絡程序設計。
二、實驗內容
- 瞭解基本的Socket知識
- 基於java完成Socket編程,設計並實現一個簡單的聊天系統,包括客戶端及服務器端。
2.1 完成服務端使用telnet與服務器通信
2.2 完成客戶端與服務端通信
2.3 完成基於多線程的實現服務端可以與多個客戶端通信 - 可選,重構現有代碼完成帶界面的軟件。實現各客戶端之間聊天,以及多人聊天室。
三、實驗方法
實驗方法爲利用Intellij IDEA開發工具,JAVA編程語言實現,參考過往實驗資料與網絡資料之後自己實現完成。
四、實驗步驟
- 開啓本機的telnet服務,在終端輸入telnet可進行連接,輸入?可查看幫助信息。
- 打開Intellij IDEA開發工具進行項目程序編寫;
2.1 實現了一個簡單的服務端: start函數負責建立服務端,等待連接,stop函數負責關閉連接以及相關的io流。Serversocket對象創建以後accept函數等待客戶端連接。若沒有客戶端連接上,則會一直輪詢,直到有客戶端連接上,繼續執行下面邏輯。當建立連接以後,雙方建立通信管道,io流讀取與寫入數據。完成socket通信。
//sampleServer.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class sampleServer {
private ServerSocket serverSocket;
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
//建立服務端,等待連接
public void start(int port) throws Exception{
serverSocket = new ServerSocket(port);
clientSocket = serverSocket.accept();
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null){
if(".".equals(inputLine)){
out.println("good bye");
stop();
break;
}else{
out.println("server got msg: "+inputLine);
}
}
}
//關閉連接以及相關的io流
public void stop() throws Exception{
in.close();
out.close();
clientSocket.close();
serverSocket.close();
}
public static void main(String[] args){
try {
sampleServer server = new sampleServer();
server.start(23);
}catch (Exception ex){
ex.printStackTrace();
}
}
}
此時,可以使用telnet連接,輸入信息,即可發送給服務端。服務端接收到處理後返回。
2.2 完成客戶端與服務端通信
在完成了可以使用telnet連接,並與之通信的服務器以後,我們編寫自己的客戶端代碼與服務器進行通信。代碼很簡單。
StartConnection函數指定ip與端口,若與服務器成功建立連接,則獲取建立的通道的io流,out與in輸入輸出流,來實現後續與服務端的消息接收與發送。
sendMessage函數基於io流發送消息。
StopConnection關閉與服務端的連接。
主函數獲得用戶在命令行的輸入,發送給服務端。
//sampleClient.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class sampleClient {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
//指定ip和端口
public void startConnection(String ip, int port)throws Exception{
clientSocket = new Socket(ip, port);
out = new PrintWriter(clientSocket.getOutputStream(),true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
}
//基於IO流發消息
public String sendMessage(String msg)throws Exception{
out.println(msg);
String resp = in.readLine();
return resp;
}
//關閉與服務端的連接
public void stopConnection(){
try {
in.close();
out.close();
clientSocket.close();
}catch (Exception ex){
ex.printStackTrace();
}
}
public static void main(String[] args){
try{
//bulid the connection
sampleClient client = new sampleClient();
client.startConnection("127.0.0.1",23);
//recevice the input string from termnal and send it to server
Scanner sc = new Scanner((System.in));
while (true){
String response = client.sendMessage(sc.nextLine());
System.out.println(response);
if(response.equals("good bye")){
client.stopConnection();
break;
}
}
}catch (Exception ex){
ex.printStackTrace();
}
}
}
2.3 完成基於多線程的實現服務端可以與多個客戶端通信
完成以上實驗,即可使用socket實現客戶端與服務端之間的交互,進行簡單的消息發送與接收。接下來我們可以實現多線程技術。
將上面服務器代碼的獲取io流,收發消息代碼移植到clientHandler類中,該類繼承自thread類。
修改start函數,依然是在本地的一個端口進行監聽,但是我們這裏使用一個循環來將每一個連接上來的client交給clientHandler來進行處理,這樣主進程就不會阻塞,可以繼續接收新客戶端的連接,從而實現一個服務端連接多個客戶端並與之通信的功能。
同時需要注意,我們使用一個clientList來維護所有的客戶端。代碼如下所示:
//clientList.java
import java.util.ArrayList;
public class clientList {
public static ArrayList clientList;
static {
clientList = new ArrayList<multiServer.clientHandler>();
}
}
//multiServer.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class multiServer {
private ServerSocket serverSocket;
public void start(int port)throws Exception{
serverSocket = new ServerSocket(port);
while (true){
clientHandler client = new clientHandler(serverSocket.accept());
clientList.clientList.add(client);
client.start();
}
}
public void stop() throws Exception{
serverSocket.close();
}
public static class clientHandler extends Thread{
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public clientHandler(Socket socket){
this.clientSocket = socket;
}
public void run(){
try{
out = new PrintWriter(clientSocket.getOutputStream(),true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null){
if(".".equals(inputLine)){
out.println("good bye");
break;
}
out.println("msg from client" + clientList.clientList.indexOf(this) + "--msg:" + inputLine);
}
in.close();
out.close();
clientSocket.close();
}catch (Exception ex){
ex.printStackTrace();
}
}
}
}
//main.java
public class main {
public static void main(String[] args){
try{
new multiServer().start(23);
}catch (Exception ex){
ex.printStackTrace();
}
}
}
五、實驗結果
實驗運行步驟及結果如下所示:
- 在Intellij IDEA中運行main函數;
- 運行命令行,輸入telnet,連接telnet服務,再輸入o,輸入連接主機 localhost;
- 接下來便可以運行第一個客戶端,客戶端即可輸入信息,服務端可以反饋;
- 重複第二步第三步即可開啓多個客戶端;
六、實驗小結
本次實驗是要是瞭解socket編程知識,客戶端與服務端的交互,多閱讀相關資料熟悉實驗中需使用的方法就會輕鬆很多。