如何利用协议编写一个简单的聊天室

1.先建立两个项目,一个是客户端,一个是服务器端。
   项目搭建如下所示:
   其中YcChart 为客户端,YcChatServer为服务器端。
项目搭建如下所示

2.编写协议类(两边的协议要一样,即自定义的YCCP协议)

package com.yc.entity;

public class YCCP {
    public static final int LOGIN = 1;      //登录处理
    public static final int SEND_MSG = 2;   //消息处理
    public static final int USER_LIST = 3;  //登录用户处理
    private int code;           // 区分不同的消息
    private String content;     // 发送的内容

    public YCCP() {
    }

    public YCCP(int code, String content) {
        this.code = code;
        this.content = content;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

3.Java是面向对象编程,所以我们把一个用户定义为一个User类(两边的User类相同)

package com.yc.entity;

public class User {
    private String name;   //用户名称
    private String ip;     //用户IP地址

    public User() {
    }

    public User(String name, String ip) {
        this.name = name;
        this.ip = ip;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }
}

4.编写服务器(Server)

package com.yc.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    private ServerSocket server;

    public static void main(String[] args) {
        new Server().startServer();
    }

    private void startServer() {
        try {
            server = new ServerSocket(9090);
            System.out.println("服务器启动,,端口9090成功...");
            while(true){    //接收多个请求客户端
                Socket client = server.accept();    //接受客户请求,没有请求就等待
                System.out.println("接收请求: " + client);
                new Thread( new ClientStack(client)).start();   //每一个客户请求,都一个线程,这样就不会相互影响了
            }
        } catch (IOException e) {
            System.err.println("服务器启动失败");
        }
    }
}

5.编写客户端接收处理类(ClientStack)

package com.yc.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;

import com.google.gson.Gson;
import com.yc.entity.User;
import com.yc.entity.YCCP;

public class ClientStack implements Runnable {
    private static List<ClientStack> clients = new ArrayList<ClientStack>();
    private static List<User> users = new ArrayList<User>();
    private Socket client;
    private Scanner in;
    private PrintWriter out;
    private User user;
    public ClientStack(Socket client) {
        try {
            this.client = client;
            in = new Scanner(client.getInputStream());  //接收客户端发送过来的数据输入流
            out = new PrintWriter( client.getOutputStream());   //向客户端发送响应数据输出流
            clients.add(this);  //加入当前请求的客户端到集合
        } catch (IOException e) {
            System.err.println( client.getInetAddress() + ":" + client.getPort() + "断开与服务器连接!!!");  //拼接客户端发送过来的数据
        }
    }

    @Override
    public void run() {
        boolean isExit= true;
        while(isExit){
            String line=null;
            YCCP yccp=null;
            try {
                line = in.nextLine(); //拼接客户端发送过来的数据
                yccp =new Gson().fromJson(line, YCCP.class);
                switch(yccp.getCode()){
                    case YCCP.LOGIN:
                        line = doLogin(yccp);
                        break;

                    case YCCP.SEND_MSG:
                        line = acceptMsg(yccp);
                        break;
                }
            } catch (Exception e) {
                line = user.getName() + "退出了聊天室...\r\n";
                clients.remove(this);
                users.remove(user);
                isExit = false;
            }   

            boardcast(YCCP.SEND_MSG,line);

            if(yccp != null && yccp.getCode() == YCCP.LOGIN || !isExit){
                boardcast(YCCP.USER_LIST, new Gson().toJson(users));
            }
        }


    }

    private void boardcast(int code,String line){
        Gson gson = new Gson();
        line = gson.toJson(new YCCP(code, line));  //转换成json格式输出
        for (ClientStack clientStack:clients) {
            clientStack.out.println(line); //给客户端写入的内容
            clientStack.out.flush();
        }
    }


    //接收发送消息处理
    private String acceptMsg(YCCP yccp) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        String msg = String.format("%s  %s : \r\n %s \r\n", user.getName(),sdf.format(new Date()),yccp.getContent());
        return msg;
    }

    //登录处理
    private String doLogin(YCCP yccp) {
        String uname = yccp.getContent();
        String ip = client.getInetAddress() +":" +client.getPort();
        user=new User(uname, ip); 
        users.add(user);
        return uname + "加入聊天室... \r\n";
    }

}

6.编写客户端ui界面(TalkView)

package com.yc.ui;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Scanner;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.wb.swt.SWTResourceManager;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.yc.entity.User;
import com.yc.entity.YCCP;

import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;


public class TalkView {
    protected Shell shell;
    private Text uname;
    private Text text_1;
    private Text text_2;
    private Text chatMessage;
    private Table table;
    private Text message;



    private Button connetBtn; // 联接
    private Button breakBtn; // 断开
    private Button sendBtn; // 发送

    private boolean flag = false;   //标识变量,控制接收消息处理

    private Socket client;
    private Scanner in;
    private PrintWriter out;
    public static void main(String[] args) {
        try {
            TalkView window = new TalkView();
            window.open();

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

    public void open() {
        Display display = Display.getDefault();
        createContents();
        shell.open();
        shell.layout();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    protected void createContents() {
        shell = new Shell();
        shell.setSize(856, 619);
        shell.setText("源辰聊天室");
        shell.setImage(SWTResourceManager.getImage(TalkView.class, "/images/yc.ico"));
        shell.setLayout(new FillLayout(SWT.HORIZONTAL));
        SashForm sashForm = new SashForm(shell, SWT.NONE);
        sashForm.setOrientation(SWT.VERTICAL);

        Group group = new Group(sashForm, SWT.NONE);
        group.setText("连接配置");

        Label label = new Label(group, SWT.NONE);
        label.setBounds(10, 38, 54, 18);
        label.setText("昵称:");

        uname = new Text(group, SWT.BORDER);
        uname.setText("random");
        uname.setBounds(78, 38, 70, 18);

        Label lblip = new Label(group, SWT.NONE);
        lblip.setBounds(177, 38, 54, 18);
        lblip.setText("服务器IP:");

        text_1 = new Text(group, SWT.BORDER);
        text_1.setText("127.0.0.1");
        text_1.setBounds(260, 38, 167, 18);

        Label label_2 = new Label(group, SWT.NONE);
        label_2.setBounds(446, 38, 54, 18);
        label_2.setText("端口");

        text_2 = new Text(group, SWT.BORDER);
        text_2.setText("9090");
        text_2.setBounds(521, 38, 70, 18);

        connetBtn = new Button(group, SWT.NONE);
        connetBtn.setBounds(614, 38, 72, 22);
        connetBtn.setText("联接");

        breakBtn = new Button(group, SWT.NONE);
        breakBtn.setEnabled(false);
        breakBtn.setBounds(719, 38, 72, 22);
        breakBtn.setText("断开");

        Group group_1 = new Group(sashForm, SWT.NONE);
        group_1.setText("聊天记录");
        group_1.setLayout(new FillLayout(SWT.HORIZONTAL));

        SashForm sashForm_1 = new SashForm(group_1, SWT.NONE);

        Composite composite = new Composite(sashForm_1, SWT.NONE);
        composite.setLayout(new FillLayout(SWT.HORIZONTAL));

        chatMessage = new Text(composite, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP
                | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
        chatMessage.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));


        Group group_3 = new Group(sashForm_1, SWT.NONE);
        group_3.setText("在线用户列表");
        group_3.setLayout(new FillLayout(SWT.HORIZONTAL));

        table = new Table(group_3, SWT.BORDER | SWT.FULL_SELECTION);
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        TableColumn tableColumn = new TableColumn(table, SWT.NONE);
        tableColumn.setWidth(150);
        tableColumn.setText("昵称");

        TableColumn tblclmnIp = new TableColumn(table, SWT.NONE);
        tblclmnIp.setWidth(200);
        tblclmnIp.setText("IP");
        sashForm_1.setWeights(new int[] { 470, 369 });

        Group group_2 = new Group(sashForm, SWT.NONE);
        group_2.setText("聊天信息");

        Label label_1 = new Label(group_2, SWT.NONE);
        label_1.setBounds(36, 29, 54, 18);
        label_1.setText("内容");

        message = new Text(group_2, SWT.BORDER);
        message.setBounds(111, 23, 519, 70);

        sendBtn = new Button(group_2, SWT.NONE);
        sendBtn.setBounds(700, 51, 72, 22);
        sendBtn.setText("发送");
        sendBtn.setEnabled(false);
        sashForm.setWeights(new int[] { 82, 384, 113 });
        doEvent();

    }

     //事件的集中地
     public void doEvent(){

         //联接
         connetBtn.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    String ipStr=text_1.getText();
                    String portStr=text_2.getText();
                    InetAddress address;
                    try {
                        address = InetAddress.getByName(ipStr);
                    } catch (UnknownHostException e1) {
                        MessageBox mb = new MessageBox(shell);
                        mb.setText("连接服务器信息");
                        mb.setMessage("服务连接地址有问题!!!");
                        mb.open();
                        return;
                    }
                    try {
                        client = new Socket(address, Integer.parseInt(portStr));    //建立与服务器的连接通道
                        flag = true;
                        in = new Scanner(client.getInputStream());  //通道的输入流
                        out = new PrintWriter(client.getOutputStream());    //通道的输出流
                        doLogin();

                        acceptMessage();
                        breakBtn.setEnabled(true);
                        connetBtn.setEnabled(false);
                    } catch (NumberFormatException | IOException e1) {
                        MessageBox mb = new MessageBox(shell);
                        mb.setText("连接服务器信息");
                        mb.setMessage("连接服务器失败,请检查服务器信息");
                        mb.open();
                        return;
                    }
                }
            });

         //断开连接
         breakBtn.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    MessageBox mb = new MessageBox(shell,SWT.YES| SWT.NO | SWT.ICON_WARNING);
                    mb.setText("连接服务器信息");
                    mb.setMessage("是否断开与服务器的连接?");
                    if(mb.open()==SWT.YES){
                            try {
                                flag = false;
                                client.close();
                                chatMessage.setText("");
                                breakBtn.setEnabled(false);
                                connetBtn.setEnabled(true);
                                table.removeAll();
                            } catch (IOException e1) {
                                // TODO Auto-generated catch block
                                e1.printStackTrace();
                            }
                    }
                }
            });

         //发送消息
         sendBtn.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    SendMessage();

                }
            });

         //发送信息框处理
         message.addModifyListener(new ModifyListener() {
                public void modifyText(ModifyEvent arg0) {
                    sendBtn.setEnabled("".intern() != message.getText().trim());
                }
            });

         //按Enter键发送
         message.addKeyListener(new KeyAdapter() {
                @Override
                public void keyReleased(KeyEvent e) {
                    if(e.keyCode==13){
                        SendMessage();
                    }
                }
            });

     }

    //接收服务器发送过来的消息
    public void acceptMessage() {
        new Thread(){
            public void run() {
                while(flag){
                    if(in.hasNextLine()){
                        String line = in.nextLine();
                        YCCP yccp =new Gson().fromJson(line, YCCP.class);
                        switch(yccp.getCode()){
                            case YCCP.USER_LIST:
                                doUserList(yccp);
                                break;

                            case YCCP.SEND_MSG:
                                doMsg(yccp);
                                break;
                        }

                    }
                }
            };
        }.start();
    }

    public void doMsg(YCCP yccp) {
        final String line = yccp.getContent();
        Display.getDefault().asyncExec(new Runnable(){
            @Override
            public void run() {
                chatMessage.setText(chatMessage.getText() + "\r\n" + line);
            }
        });
    }

    public void doUserList(YCCP yccp) {
        Gson gson = new Gson();
        Type t=new TypeToken<List<User>>(){}.getType(); //json复杂类型的转换
        final List<User> users = gson.fromJson(yccp.getContent(), t);
        Display.getDefault().asyncExec(new Runnable(){
            @Override
            public void run() {
                table.removeAll();
                for(User user : users){
                    TableItem ti = new TableItem(table,SWT.NONE);
                    ti.setText(new String[]{user.getName(),user.getIp()});
                }
            }
        });

    }



    //向服务器发送消息
    public void SendMessage() {
        YCCP yccp = new YCCP(YCCP.SEND_MSG,message.getText());
        String msg = new Gson().toJson(yccp);
        out.println(msg);
        out.flush();
        message.setText("");
    }

    public void SendMessage(String msg) {
        out.println(msg);
        out.flush();
    }

    //第一次连接做登录
    public void doLogin(){
        String user = uname.getText();
        YCCP yccp = new YCCP(YCCP.LOGIN,user);
        SendMessage(new Gson().toJson(yccp));
    }
}
利用C语言编写基于TCP协议聊天室程序,大致可以分为以下几个步骤: 1. 网络编程基础:首先需要了解TCP协议的工作原理,以及C语言网络编程的相关API。TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,它提供全双工服务,并且是面向字节流的。 2. 套接字(Socket)编程:在C语言,可以使用套接字来实现网络通信。创建套接字,需要指定地址族(Address Family)、套接字类型和协议类型。对于TCP协议,地址族通常是AF_INET(IPv4地址),套接字类型是SOCK_STREAM,协议类型是IPPROTO_TCP。 3. 服务器端实现: - 创建套接字,绑定IP地址和端口号。 - 监听连接请求。 - 接受客户端连接请求。 - 读取和发送数据。 - 关闭套接字。 4. 客户端实现: - 创建套接字。 - 连接到服务器的IP地址和端口号。 - 发送和接收数据。 - 关闭套接字。 5. 多线程或多进程:为了能够同时处理多个客户端连接,服务器端通常会使用多线程或多进程技术。每个客户端连接可以由一个线程或进程来处理,这样可以实现并发通信。 6. 完善功能:为了构建一个完整的聊天室程序,还需要考虑数据的封装、协议设计(比如聊天消息的格式)、用户管理、会话管理等。 以下是一个简化的示例代码框架,展示了基于TCP的简单聊天室服务器端的核心步骤: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <pthread.h> #define PORT 8080 #define BUFFER_SIZE 1024 void* handle_client(void* arg); int main(int argc, char *argv[]) { int sockfd, new_sockfd; socklen_t clilen; char buffer[BUFFER_SIZE]; struct sockaddr_in serv_addr, cli_addr; pthread_t thread_id; sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(PORT); bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); listen(sockfd, 10); clilen = sizeof(cli_addr); while(1) { new_sockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &clilen); if(new_sockfd < 0) { perror("ERROR on accept"); continue; } if(pthread_create(&thread_id, NULL, handle_client, (void*)&new_sockfd) < 0) { perror("ERROR creating thread"); close(new_sockfd); continue; } } return 0; } void* handle_client(void* arg) { int sockfd = *((int*)arg); char buffer[BUFFER_SIZE]; int read_size; // 读取数据并发送给客户端 while(1) { memset(buffer, '\0', BUFFER_SIZE); read_size = read(sockfd, buffer, BUFFER_SIZE - 1); if(read_size == 0) { printf("Client closed the connection\n"); break; } else if(read_size > 0) { printf("From client: %s", buffer); // 发送数据给客户端,这里假设只是简单地回显 write(sockfd, buffer, strlen(buffer)); } } close(sockfd); return NULL; } ``` 注意:上述代码仅为示例,没有包含错误处理、用户交互、多线程同步等完整聊天室所需的全部功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值