Java菜鸡的通信之旅

       

先从一小段简单代码开始:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;


public class MyServer {
    static int port = 8888;
    InputStream is;
    OutputStream os;
    public static void main(String[] args) throws IOException {
            new MyServer().createServer(port);

    }
    public void createServer(int port) throws IOException {
        //在本地建立一个服务器的套接字(套接字内容为本设备IP地址加port赋值的端口号)
        ServerSocket server = new ServerSocket(port);
        System.out.println("创建服务器成功,等待客户端链接。。。");

        //监听链接此服务器的客户端,该方法为阻塞方法
        Socket socket = server.accept();

        //通过socket获取输入输出
         is = socket.getInputStream();
         os = socket.getOutputStream();

        //为了达成自由的输入输出,这里使用了多线程
        InputThread inPut = new InputThread();
        OutputThread outPut = new OutputThread();
        outPut.start();
        inPut.start();
    }
    
    //输入流线程
    private class InputThread extends Thread {
        @Override
        public void run() {
            char c;
            String[] s = new String[10];
            String[] p = new String[10];
            int i = 0;
            while (true){
                //读取客户发来的信息
                try {
                    c = (char)is.read();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                if(c == 13 ) {
                    s[i] = s[i] + "\r\n" ;
                    System.out.print(s[i]);
                    i++;
                    if(i % 10 == 0){
                        //数据记录的扩容
                        for(int j = 0 ; j < i ;j++){
                            p[j] = s[j];
                        }
                        s = new String[i + 10];
                        for(int j = 0 ; j < i ;j++){
                            s[j] = p[j];
                        }
                        p = new String[i + 10];}}
                s[i] = s[i] + c;}}}

    //输出流线程
    private class OutputThread extends Thread{
        @Override
        public void run() {
            Scanner sc = new Scanner(System.in);
            String s ;
            while (true){
                s = sc.nextLine();
                try {
                    s = s + "\r\n";
                    os.write(s.getBytes());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }}}}

在运行该程序后,用WIN+R快捷键打开运行,输入cmd,打开命令指示框。

在命令指示框中输入:telnet localhost 8888(这段数字取决于代码中port的值),即可在命令指示框与代码运行窗口直接进行通信测试。

在这段程序中,数据通信的过程为基本的服务器-客户端双向通信:

 程序中出现的数个概念解释:

服务器(Server):

        一般情况下提供服务的一端

客户端(Clinic):

        一般情况下接受服务的一端

TCP/IP协议:

        全称 Transmission Control Protocol/Internet Protocol 传输控制协议/网际协议,是网络通信最基本的协议。

        其中IP协议是指数据传输时,信息的发送端将数据传输到接收端。可以将网络通信想象成现实中的物流系统,IP协议就是:当某人(信息发送端)将包裹投入快递驿站后,整个物流系统会根据信封上的地址(IP地址)找到正确的收件人(接收端)。

        TCP系统则可以视为物流系统的打包和签收服务。当包裹太大太重不好搬运时时,工作人员会对你的包裹进行拆解(TCP提供可靠字节流服务,即将大块数据拆解成以segment为单位的数段方面运输管理),并且安排送上门的签收服务。快递小哥在根据IP地址找到接收者地址后,会敲门询问:您是XXX吗?客户:是的。小哥:好的,请开一下门(建立联系)签收快递(数据传输)。(为保障数据可靠运输而设的步骤,程序上俗称“三次握手”)

端口(Port):借用上述使用过的快递例子,端口可被视为门牌号。当由客户端向服务器发出申请时,不同的数据应该被发送至服务器不同程序中进行处理。数据进入这些程序的虚拟入口就可以被称为端口。为方便区分查找为诸多端口编号,其序号就是端口号。

套接字(Socket,直译为 插座):输入输出流会通过socket进出服务器和客户端。

        在组成上,socket = IP  :Port

(例如IP 地址=192.201.1.234,端口 = 31,则socket = 192.201.1.234.31)

        故可以通过对socket的操作获取IO流

接下来就该实现多人围绕本地服务器的聊天室功能:

将原来的一服务器对一客户端的流程封装成一个独立线程,并通过共有的socket表和user表实现私聊和群聊,本代码属于想到哪里写到哪里,所以显得比较驳杂

import javax.sound.sampled.LineListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
public class MyServer {
    static int port = 8888;
    private InputStream is;
    private OutputStream os;
    private int num = 0;
    private List<String> privateList = new LinkedList<>();
    private List<Socket> socketList = new LinkedList<>();
    private List<User> userList = new LinkedList<>();
    private boolean privateChat = false;

    public static void main(String[] args) throws IOException {
        new MyServer().createServer(port);


    }
    public void createServer(int port) throws IOException {
        ServerSocket server = new ServerSocket(port);
        System.out.println("The server is successfully founded,waiting for users'connecting request。。。");

        //监听链接此服务器的客户端,该方法为阻塞方法
        int num = 0;
        while(true){
            Socket socket = server.accept();
            socketList.add(socket);
            Runnable run = new usersThread(socket,num);
            Thread thread = new Thread(run);
            thread.start();
            num++;
        }
    }
    //将字节流转换成字符串
    public String readMsg(InputStream is )throws IOException{
        StringBuilder stringBuilder = new StringBuilder();
        int i = 0;int j = 0;
        String s = "";
        boolean check = false;
        while((i = is.read()) != 13){
            if(i == '@'){
                check = !check;
                if(!check) {
                    privateList.add(s);
                    s = "";
                    privateChat = true;
                }
            }else if(check){
                s = s + (char)i;
            }
            stringBuilder.append((char)i);
        }
        return stringBuilder.toString().trim();
    }


    //创建用户信息
    private class User{
        public String nickName;
        public Socket socket;
        private int num;
        public User(String nickName,Socket socket,int num){
            this.nickName = nickName;
            this.socket = socket;
            this.num = num;
        }
    }
    private class usersThread  implements Runnable{
        private InputStream is ;
        private User user;
        private OutputStream os ;

        //单个用户线程
        public usersThread(Socket socket,int num) throws IOException {
            //短期账户创建步骤(登记昵称)
            is = socket.getInputStream();
            os =socket.getOutputStream();
            String msg = "Please enter your temporary nickname:\r\n";
            os.write(msg.getBytes());
            user = new User(readMsg(is) ,socket,num);
            userList.add(user);
            msg ="Welcome" + "  " + user.nickName + "\r\n";
            os.write(msg.getBytes());
        }
        @Override
        public void run() {
            System.out .println("user:" + user.nickName + " enter the room");
            while(true){
                try{

                    String s = readMsg(is);
                    System.out.println("user " + user.nickName + " says:" + s + "\r\n");
                    for(int i = 0;i <socketList.size();i++){
                        Socket socket = socketList.get(i);
                        //私聊代码
                        if(privateChat){
                            checkIfExist(user.socket,s);
                            privateChat = false;
                            privateList = new LinkedList<String>();
                            break;
                        }
                        //群聊代码
                        if(socket != user.socket){
                            os = socket.getOutputStream();
                            os.write(("user " + user.nickName + " says: " + s + "\r\n").getBytes());
                        }
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    //私聊对象是否存在?
    private void checkIfExist(Socket socket,String s) throws IOException {
        for(int i = 0;i < privateList.size();i++){
            for(int j = 0 ; j < userList.size();j++){
                //遍历寻找私聊对象,获取其socket值
                if(privateList.get(i).equals(userList.get(j).nickName)){
                    os = userList.get(j).socket.getOutputStream();
                    os.write(("user " + userList.get(j).nickName + " says to you privately: " + s + "\r\n").getBytes());
                    break;
                }
                System.out.print(privateList.get(i)+":");
                System.out.println(userList.get(j).nickName);

            }
            os = socket.getOutputStream();
            os.write((privateList.get(i)+" doesn't exist.\r\n").getBytes());
        }
    }
}

        在测试该段代码时,如果某客户端断开连接,会标识报错:Space Heap。其原因是我们使用的客户端与所写的程序底层存在不兼容。经测试,如果是自己写地客户端就不会报该错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值