如何用Java编写一个简单的服务器和客户机
Posted 小大亨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用Java编写一个简单的服务器和客户机相关的知识,希望对你有一定的参考价值。
小编先从客户机和服务器的模型开始讲解。简单来说,我们实现的这种模型呢,我们每一个用户称为一个客户机,用户之间的通信之间需要一个中转,所有客户机的通信都依托于这个中转,很明显,这个中转,就是砸门的服务器了。整个模型比较简单明了,那么,接下来我们就直接进入实现阶段。
我们从实现服务器开始。Java提供了这样的一个类,ServerSocket。我们通过实例化它的方式来创建一个服务器。注意一下,创建服务器需要一个参数port。port指的是端口号。我们知道一台计算机上运行着许多程序,那么,对于另一台计算机来说,它想要连接到这台服务器的这个程序,该如何区分呢?首先当然是根据IP连接到这台计算机,那么,然后呢?对的,你没有想错,就是通过我们的port端口号了。正常情况下,计算机会自动我们创建的程序分配一个port,但是,因为我们创建的是提供给别的计算机连接的服务器,所以我们需要指定一个端口号供给外部计算机连接。注意一下,计算机有的端口号已经被其他程序占用了,要想查看,你可以通过在cmd命令行输入netstat -an来查询。在这里,我们给定我们的端口号为空闲的9999。
1 public static void main(String[] args) {
2 new Server().CreateServer(9999);
3 }
4 public void CreateServer(int port){
5 try {
6 ServerSocket serversocket = new ServerSocket(port);
7 } catch (IOException e) {
8 e.printStackTrace();
9 }
10 }
接下来,我们让服务器进入等待客户机连接的状态。通过查看ServerSocket源码我们会发现,它里面有一个accept方法,其返回值是Socket类型。
1 Socket socket = serversocket.accept();
Socket是Java中的另一个用于通信的类, 我们称它为套接字。事实上,小编是这样看的,Socket相当于建立在服务器和客户端之间的一根通道。我们可以从socket获取它的输入输出流对象,也就是我们的InputStream和OutputStream。这两个输入输出都是采用字节传输的方法,如果你想要读取一行数据可以通过BufferReader bufferreader = new BufferedReader(new InputStreamReader(is))的方式,将InputStream转化成bufferRead之后调用readLine方法就可以了。需要注意的是对于readLine这个方法,/r/n表示消息的读取结束。因此需要对消息进行+“/r/n”的操作来保证读取的正常进行。
因为在等待客户机连接的时候,程序进入阻塞状态,这个时候程序无法进行下去,而且,这样的写法也只能够供给一个客户机连接。为了能够实现简单客户机和服务器的对话,我们需要采用多线程的方式去实现它,并且将实例化socket对象以及之后的这部分代码放入到while循环当中。为了能够实现客户机与客户机的通信,我们建立了一个消息处理类,并且实例化了一个数组队列去存储它,然后将这个队列作为消息处理类的一个属性。这样所有的问题就得到了完美的解决。
1 public void creatserver(int port) {
2 try {
3 ServerSocket serversocket = new ServerSocket(port);
4 System.out.println("服务器已经开启,正在等待客户机连接...");
5 while (true) {
6 Socket sk = serversocket.accept();
7 chatMessage cm = new chatMessage(sk, list);
8 list.add(cm);
9 cm.start();
10 }
11 } catch (IOException e) {
12 e.printStackTrace();
13 }
14 }
消息处理类当中的内容,主要是实现对消息的封装处理等等,以及对消息的鉴别。诸如消息是群发还是私发,如果是群发,那么遍历整个消息处理类的队列,如果是私发,那么同样需要遍历整个队列去寻找我们需要私发的用户名,然后将消息传给它。一些其他的注意就不再赘述了,小编在代码中也给出了相应的注释,特别提出一点,当socket关闭,连接断开,我们同样需要关闭我们的IO流,否则占用资源是比较严重的。
1 public class chatMessage extends Thread {
2 // 客户机的账号
3 public String username;
4 // 客户机的密码
5 public String password;
6 // // 客户机的开启状态
7 // public volatile boolean state;
8 // 客户机对象
9 public Socket s;
10 // 客户机的输入流
11 public InputStream is;
12 // 客户机的输出流
13 public OutputStream os;
14 // 缓冲(读一行数据)
15 public BufferedReader br;
16 // 存储所有客户机对象的数组队列
17 public ArrayList<chatMessage> list;
18
19 /**
20 * chatMessage的构造方法,包括IO流的建立
21 *
22 * @param s
23 * Socket对象
24 * @param list
25 * 存储chatMessage对象的数组队列
26 * @throws IOException
27 * 抛出IO异常
28 */
29 public chatMessage(Socket s, ArrayList<chatMessage> list)
30 throws IOException {
31 this.s = s;
32 this.list = list;
33 is = s.getInputStream();
34 os = s.getOutputStream();
35 br = new BufferedReader(new InputStreamReader(is));
36 }
37
38 /**
39 * 服务器转发消息
40 *
41 * @param str
42 * 转发的消息内容
43 * @throws IOException
44 * 抛出IO异常
45 */
46 public void sendMessage(String str) throws IOException {
47 os.write((str + "\r\n").getBytes());
48 }
49
50 /**
51 * 服务器把消息广播到所有的客户机
52 *
53 * @param str
54 * 广播的消息内容
55 * @param code
56 * 消息类型的判断,1表示为提示成员登录,2表示为广播消息
57 * @throws IOException
58 * 抛出的IO异常
59 */
60 public void sendAllMessage(String str, int code) throws IOException {
61 for (int index = 0; index < list.size(); index++) {
62 if (list.get(index) != this && code == 1) {
63 list.get(index).sendMessage(str);
64 } else if (code == 2) {
65 list.get(index).sendMessage(str);
66 }
67 }
68 System.out.print(str + "\r\n");
69 }
70
71 /**
72 * 读取来自客户端的消息
73 *
74 * @return 返回一个字符串
75 * @throws IOException
76 * 抛出IO异常
77 */
78 public String readMessage() throws IOException {
79 return br.readLine();
80 }
81
82 /**
83 * 服务器把消息转发给某人
84 *
85 * @param str1
86 * 消息内容
87 * @param str2
88 * 消息的接收人
89 */
90 public void sendMessageToOthers(String str1, String str2) {
91 for (int i = 0; i < list.size(); i++) {
92