您的位置:首页 > 其它

使用ServerSocket、Socket创建一个简单的服务聊天程序

2015-09-07 23:25 856 查看
使用ServerSocket创建TCP服务器端:

在两个实心实体没有建立虚拟链路之前,必须有一个通信实体先做出“主动姿态”,主动接收来自其他通信实体的连接请求。

ServerSocket在Java中能接收到其他通信的连接请求,ServerSocket对象用于监听来自客户端Socket的连接请求,ServerSocket使用完毕后再使用它的close()方法将ServerSocket关闭,一般服务端要接收好多个客户端的连接请求,所以程序中通常会用while循环不断的调用ServerSocket的accept()方法来接收客户端的请求。

ServerSocket server=new ServerSocket(8088);
//当接收到客户端的连接请求时,服务端就会产生一个Socket
//为了让服务端不断的接收来自客户端的请求,所以都是用while循环
while(true){
Socket s=server.accept();
}


上面的客户端通信就是用Socket来连接到服务端的,经常用的就是

Socket socket=new Socket(InetAddress/String remoteAddress,int port);


因为本地主机只有一个IP地址,所有使用这个比较方便简单

本地IP地址使用的是127.0.0.1,这个还是比较特殊的,它总是代表着本机的IP地址哈。

当客户端和服务端产生了对应的Socket后,程序也就不再区分什么服务器端和客户端,直接通过各自的Socket进行通信。

基于这个可以做一个简单的通信代码,当你连接上服务端后,服务器会向所有在线的客户端广播*******on Line,然后你无论说什么,服务端就像一个大喇叭给你广播出去给所有客户端看。





package Chat;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
* 客户端
*
* Java.net.Socket
* 封装了TCP协议的Socket
* 通过它来连接服务端的ServerSocket并创建输入输出流来与服务端通信
* @author Administrator
*
*/
public class Client {

private Socket socket;
/*
* 构造方法,用来初始化客户端
* 构造方法常用来初始化对象属性等操作
*/

public Client(){
try {

/*
* 初始Socket时需要传入两个参数
* 1.服务端IP地址
* 2.服务端端口号
*
* 首先要清楚:
* 通讯是客户端计算机上的一个客户端应用程序与服务端计算机(俗称服务器)上的一个服务端
* 应用程序之间的通讯
*
* IP地址的作用是让我们通过网络可以找到服务器,而端口可以让我们找到运行在服务器上的服务端
* 应用程序。
*
* 创建Socket实例的过程就是与服务端连接的过程,若可以成功与服务器连接上,则会创建Socket
* 实例,否则构造方法会抛出异常。
*/

System.out.println("正在尝试连接服务端……");
socket=new Socket("localhost",8088);
System.out.println("与服务端连接成功");

} catch (Exception e) {
e.printStackTrace();
}
}

/*
* 客户端开始工作的方法
*/
public void start(){

try {
/*
* 当客户端启动后,就启动接收服务端发送过来消息的线程
*/
GetServerMessageHandler handler=new GetServerMessageHandler();
new Thread(handler).start();
/*
* OutputStream getOutputStream()
* Socket提供了该方法,用来获取输出流来向服务端发送数据。
*/
Scanner scan=new Scanner(System.in);
OutputStream out=socket.getOutputStream();

OutputStreamWriter osw=new OutputStreamWriter(out,"UTF-8");
PrintWriter pw=new PrintWriter(osw,true);
System.out.println("客户端输入信息:");
while(true){
String message=scan.next();
pw.println(message);
}
} catch (Exception e) {
e.printStackTrace();
}

}

public static void main(String[] args) {
Client client = new Client();
client.start();
}

/**
* 由于接收服务端发送来的消息,与我们给服务端发送消息没有必然关系,所以两者应当在两个不同
* 线程上完成,各做各的,互不干涉。
* @author Administrator
*
*/
private class GetServerMessageHandler implements Runnable{
public void run(){
try {

/*
* 该线程的职责就是读取服务端发送过来的每一条消息,并输出到控制台
*/
InputStream in=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(in,"utf-8");
BufferedReader br=new BufferedReader(isr);
String message=null;
while((message=br.readLine())!=null){
System.out.println("服务器说:"+message);
}

} catch (Exception e) {

}
}
}
}


程序写的有点繁琐,上面那个是客户端的代码,下面这个是服务端的代码:

package Chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
* 聊天室服务端
*
* java.net.ServerSocket
* ServerSocket是运行在服务端的,其作用是向系统申请服务端端口,以便监听该端口,等待客户端的连接,
* 一旦一个客户端连接,就会创建一个Socket与该客户端进行通信。
* @author Administrator
*
*/
public class Server {
private ServerSocket server;//ServerSocket对象用于监听来自客户端的Socket连接,
//ServerSocket包含一个 监听来自客户端连接请求的方法
//存放所有客户端输出流的集合,用于广播信息
private List<PrintWriter> allOut;
public  Server(){
try {
allOut=new ArrayList<PrintWriter>();
/*
* 初始化ServerSocket的同时需要指定服务端口,该端口不能与当前系统使用TCP协议的
* 其他程序申请的端口冲突,否则会抛出端口被占用异常
*/
server = new ServerSocket(8088);

} catch (Exception e) {
e.printStackTrace();
}

}

private synchronized void addOut(PrintWriter pw){
allOut.add(pw);
}
private synchronized void removeOut(PrintWriter pw){
allOut.remove(pw);
}
private synchronized void sendMessageToAllClient(String m){
for(PrintWriter pw:allOut){
pw.println(m);
}
}

//服务端开始工作的方法
public void start(){
try {

/*
* Socket accept()
* ServerSocket提供该方法用来监听打开服端口(8088),该方法是一个阻塞方法,
* 直到一个客户端尝试连接才会解除阻塞,并创建一个Socket与刚连接的客户端进行通讯。
*
* accept方法每次调用都会等待一个客户端的连接,所以若希望让若干个客户端连接,就需要多次
* 调用该方法,来分别获取对应这些客户端
* 的socket与他们通讯。
*/

while(true){
System.out.println("等待客户端连接……");
Socket socket=server.accept();
System.out.println("一个客户端连接了!");
/**
* 当一个客户端连接后,启动一个线程,来负责与该客户端交互
*/
ClientHandler handler=new ClientHandler(socket);
new Thread(handler).start();
}

} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server=new Server();
server.start();

}

/**
*
* 该线程用来与一个指定的客户端进行交互
* 每当一个客户端连接服务端后,都会启动当前线程来负责与之交互工作。
*/
private class ClientHandler implements Runnable{
private Socket  socket;

//客户端地址信息
private String host;

public ClientHandler(Socket socket){
this.socket=socket;
//通过socket可以得知远端计算机信息
InetAddress address=socket.getInetAddress();
host=address.getHostAddress();
}
public void run(){
PrintWriter pw=null;
try {
/*
* 通过客户端的Socket获取输出流,以便将消息发送给客户端
*/
OutputStream out=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(out,"UTF-8");
pw=new PrintWriter(osw,true);

//共享该客户端的输出流
addOut(pw);

//广播该用户上线
sendMessageToAllClient(host+"onLine");

/*
* InputStream getInputStream()
* Socket提供的该方法用来获取输入流,读取远端计算机发送过来的数据
*/
InputStream in=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(in,"utf-8");
BufferedReader br=new BufferedReader(isr);

String message=null;
/*
* 当我们使用BufferedReader读取来自远端计算机发送过来的内容时,由于远端计算机的操作系统不同,
* 当他们断开连接时,这里readline方法的结果也不同:
* 当远端计算机操作系统是Windows时,若断开连接,这里的readline方法直接会抛出异常。
* 当远端计算机操作系统是Linux时,若断开连接,这里的readline方法返回NULL。
*/
while((message=br.readLine())!=null){
sendMessageToAllClient(host+"说:"+message);
}

} catch (Exception e) {
e.printStackTrace();
}finally{
/*
* 当该客户端与服务端断开时,应当将该客户端的输出流从共享集合删除。
*/
allOut.remove(pw);

/**
* 无论是Linux的客户端还是Windows的客户端,当与服务器断开连
* 接后,都应当将与该客户端交互的 socket关闭,来释放底层资源。
*/
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: