socket-client share message

本文介绍了一个基于Java实现的简易聊天服务器的设计与实现细节。该服务器能够接收多个客户端连接,并通过广播方式转发消息到所有在线用户。文章展示了如何使用Java Socket API进行网络通信,包括服务器端与客户端的消息收发流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Server:

 

import java.net.*;

import java.io.*;

import java.util.*;

 

public class Server extends ServerSocket {

private Socket socket;

private static final int SERVER_PORT = 6789;

private static ArrayList User_List = new ArrayList();

private static ArrayList Threader = new ArrayList();

private static LinkedList Message_Array = new LinkedList();

private static int Thread_Counter = 0;

private static boolean isClear = true;

protected FileOutputStream LOG_FILE = new FileOutputStream("E:/program/connect.log",true);

public Server() throws IOException,FileNotFoundException

{

super(SERVER_PORT);

new Broadcast();

Calendar now = Calendar.getInstance();

String str = "[" + now.getTime().toString() + " ] Accepted a connection1512";

byte[] tmp = str.getBytes();

LOG_FILE.write(tmp);

try

{

while(true)

{

socket = accept();

new CreateServerThread(socket);

}

}

catch(IOException e)

{}

finally

{

close();

}

}

class Broadcast extends Thread

{

public Broadcast()

{

start();

}

public void run()

{

while(true)

{

if(!isClear)

{

String tmp = (String)Message_Array.getFirst();

for(int i = 0; i< Threader.size();i++)

{

CreateServerThread client = (CreateServerThread)Threader.get(i);

client.SendMessage(tmp);

}

Message_Array.removeFirst();

isClear = Message_Array.size()>0 ? false:true;

}

}

}

}

class CreateServerThread extends Thread

{

private Socket client;

private BufferedReader in;

private PrintWriter out;

private String username;

public CreateServerThread(Socket s) throws IOException

{

client = s;

in = new BufferedReader(new InputStreamReader(client.getInputStream()));

out = new PrintWriter(client.getOutputStream(),true);

out.println("--Welcome to this chatroom--");

out.println("input your nickname:");

start();

}

public void SendMessage(String msg)

{

out.println(msg);

}

public void run()

{

try

{

int flag = 0;

Thread_Counter++;

String line = in.readLine();

while(!line.equals("bye"))

{

if(line.equals("1"))

{

out.println(ListOnlineUsers());

line = in.readLine();

continue;

}

if(flag++ == 0)

{

username = line;

User_List.add(username);

out.println(ListOnlineUsers());

Threader.add(this);

PushMessage("[< "+username+" come on in >]");

}

else

{

PushMessage("< "+ username + " >" + line);

}

line = in.readLine();

}

out.println("--see you,bye--");

client.close();

}

catch(IOException e)

{}

finally

{

try

{

client.close();

}

catch(IOException e)

{}

Thread_Counter --;

Threader.remove(this);

User_List.remove(username);

PushMessage("[< " + username + " left>]" );

}

}

private String ListOnlineUsers()

{

String s = "-+- Online list -+-1512";

for (int i = 0 ; i < User_List.size(); i ++)

{

s+="[" + User_List.get(i) + "]1512";

}

s+="-+-----------------------+-";

return s;

}

private void PushMessage(String msg)

{

Message_Array.addLast(msg);

isClear = false;

}

}

 

/**

* @param args

*/

public static void main(String[] args) throws IOException {

// TODO Auto-generated method stub

new Server();

 

}

 

}

 

Client:

 

same as single client

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #define PORT 8080 #define BUFFER_SIZE 4096 #define FILE_PATH "/usr/share/data/linelaser.bin" void error(const char *msg) { perror(msg); exit(EXIT_FAILURE); } // 确保目录存在 void ensure_directory_exists() { char path[256] = {0}; strncpy(path, FILE_PATH, sizeof(path)); // 提取目录路径 char *last_slash = strrchr(path, '/'); if (last_slash) *last_slash = '\0'; // 创建目录(如果不存在) struct stat st = {0}; if (stat(path, &st) == -1) { if (mkdir(path, 0755) == -1) { perror("mkdir failed"); exit(EXIT_FAILURE); } printf("Created directory: %s\n", path); } } int main() { ensure_directory_exists(); int server_fd, client_fd; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[BUFFER_SIZE] = {0}; // 创建TCP套接字 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { error("socket failed"); } // 设置套接字选项 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { error("setsockopt failed"); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定端口 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { error("bind failed"); } // 监听连接 if (listen(server_fd, 3) < 0) { error("listen failed"); } printf("Server listening on port %d\n", PORT); printf("Target file: %s\n", FILE_PATH); while (1) { // 接受客户端连接 if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept failed"); continue; } printf("Client connected\n"); // 接收命令 ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1); if (bytes_read < 0) { perror("read failed"); close(client_fd); continue; } buffer[bytes_read] = '\0'; printf("Received command: %.*s...\n", 20, buffer); // 只打印前20个字符 // 只处理特定文件 if (strcmp(buffer, "READ") == 0) { // 读取文件 int fd = open(FILE_PATH, O_RDONLY); if (fd < 0) { perror("open file failed"); send(client_fd, "ERROR:File not found", 21, 0); } else { // 获取文件大小 struct stat file_stat; if (fstat(fd, &file_stat) < 0) { perror("fstat failed"); close(fd); send(client_fd, "ERROR:File stat failed", 23, 0); close(client_fd); continue; } // 发送文件大小信息 char size_msg[64]; snprintf(size_msg, sizeof(size_msg), "SIZE:%ld", file_stat.st_size); if (send(client_fd, size_msg, strlen(size_msg), 0) < 0) { perror("send size failed"); } // 发送文件内容 ssize_t total_sent = 0; while ((bytes_read = read(fd, buffer, BUFFER_SIZE)) > 0) { ssize_t sent = send(client_fd, buffer, bytes_read, 0); if (sent < 0) { perror("send failed"); break; } total_sent += sent; } close(fd); printf("Sent %ld bytes to client\n", total_sent); } } else if (strncmp(buffer, "WRITE:", 6) == 0) { // 写入文件 int fd = open(FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { perror("open for write failed"); send(client_fd, "ERROR:Write failed", 19, 0); close(client_fd); continue; } // 已接收的数据(包含命令前缀) ssize_t total_written = 0; ssize_t data_offset = 6; // "WRITE:" 的长度 ssize_t data_length = bytes_read - data_offset; // 写入已接收的数据 if (data_length > 0) { ssize_t written = write(fd, buffer + data_offset, data_length); if (written != data_length) { perror("write initial data failed"); close(fd); send(client_fd, "ERROR:Write error", 18, 0); close(client_fd); continue; } total_written += written; printf("Wrote initial %ld bytes\n", written); } // 继续接收剩余数据 while (1) { bytes_read = read(client_fd, buffer, BUFFER_SIZE); if (bytes_read == 0) { // 客户端关闭连接 break; } else if (bytes_read < 0) { perror("read during write failed"); break; } ssize_t written = write(fd, buffer, bytes_read); if (written < 0) { perror("write failed"); break; } total_written += written; printf("Wrote %ld bytes (total %ld)\n", written, total_written); } // 确保文件写入磁盘 fsync(fd); close(fd); // 发送响应 char success_msg[64]; snprintf(success_msg, sizeof(success_msg), "OK:Written %ld bytes", total_written); if (send(client_fd, success_msg, strlen(success_msg), 0) < 0) { perror("send success failed"); } else { printf("Sent confirmation: %s\n", success_msg); } printf("Received and wrote %ld bytes to %s\n", total_written, FILE_PATH); } else { send(client_fd, "ERROR:Invalid command", 22, 0); } // 关闭客户端连接 shutdown(client_fd, SHUT_RDWR); close(client_fd); printf("Client disconnected\n"); } close(server_fd); return 0; }这是linux端服务器代码, #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTcpSocket> #include <QFile> #include <QElapsedTimer> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_connectButton_clicked(); void on_downloadButton_clicked(); void on_uploadButton_clicked(); void socketConnected(); void socketDisconnected(); void socketReadyRead(); void socketError(QAbstractSocket::SocketError error); private: Ui::MainWindow *ui; QTcpSocket *socket; QByteArray fileData; bool isReceivingFile; qint64 fileSize; qint64 bytesReceived; bool waitingForUploadConfirm; QElapsedTimer uploadTimer; void logMessage(const QString &message); void setUIEnabled(bool enabled); void resetTransfer(); }; #endif // MAINWINDOW_H这是qt客户端头文件 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QMessageBox> #include <QSettings> #include <QDateTime> #include <QTimer> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , socket(new QTcpSocket(this)) , isReceivingFile(false) , fileSize(0) , bytesReceived(0) , waitingForUploadConfirm(false) { ui->setupUi(this); setWindowTitle("ARM File Transfer Client"); // 连接信号槽 connect(socket, &QTcpSocket::connected, this, &MainWindow::socketConnected); connect(socket, &QTcpSocket::disconnected, this, &MainWindow::socketDisconnected); connect(socket, &QTcpSocket::readyRead, this, &MainWindow::socketReadyRead); connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); // 加载设置 QSettings settings; ui->ipEdit->setText(settings.value("serverIP", "192.168.1.100").toString()); ui->portEdit->setText(settings.value("serverPort", "8080").toString()); // 初始状态 setUIEnabled(false); ui->statusLabel->setText("未连接"); ui->progressBar->setValue(0); } MainWindow::~MainWindow() { // 保存设置 QSettings settings; settings.setValue("serverIP", ui->ipEdit->text()); settings.setValue("serverPort", ui->portEdit->text()); delete ui; } void MainWindow::on_connectButton_clicked() { if (socket->state() == QAbstractSocket::ConnectedState) { socket->disconnectFromHost(); resetTransfer(); return; } // 重置状态 resetTransfer(); // 连接服务器 QString ip = ui->ipEdit->text(); quint16 port = ui->portEdit->text().toUShort(); if (ip.isEmpty()) { logMessage("错误:请输入服务器IP地址"); return; } setUIEnabled(false); logMessage(QString("正在连接 %1:%2...").arg(ip).arg(port)); socket->connectToHost(ip, port); } void MainWindow::on_downloadButton_clicked() { if (socket->state() != QAbstractSocket::ConnectedState) { logMessage("错误:未连接到服务器"); return; } resetTransfer(); logMessage("请求下载文件..."); // 发送READ命令 if (socket->write("READ", 4) == -1) { logMessage("发送命令失败: " + socket->errorString()); return; } isReceivingFile = true; ui->progressBar->setValue(0); } void MainWindow::on_uploadButton_clicked() { if (socket->state() != QAbstractSocket::ConnectedState) { logMessage("错误:未连接到服务器"); return; } // 打开本地文件 QString filePath = QFileDialog::getOpenFileName(this, "选择要上传的文件", "", "二进制文件 (*.bin)"); if (filePath.isEmpty()) return; QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { logMessage("错误:无法打开文件 - " + file.errorString()); return; } // 读取文件内容 fileData = file.readAll(); file.close(); if (fileData.isEmpty()) { logMessage("错误:文件内容为空"); return; } // 准备上传 resetTransfer(); logMessage(QString("准备上传文件 (%1 字节)...").arg(fileData.size())); // 构造WRITE命令前缀 QByteArray commandPrefix = "WRITE:"; // 发送命令前缀 if (socket->write(commandPrefix) != commandPrefix.size()) { logMessage("发送命令前缀失败: " + socket->errorString()); return; } // 分块发送文件数据 qint64 totalSize = fileData.size(); qint64 bytesSent = 0; qint64 chunkSize = 4096; // 每次发送4KB uploadTimer.start(); // 开始计时 while (bytesSent < totalSize) { QByteArray chunk = fileData.mid(bytesSent, chunkSize); qint64 written = socket->write(chunk); if (written == -1) { logMessage("发送文件数据失败: " + socket->errorString()); return; } // 等待数据写入缓冲区 if (!socket->waitForBytesWritten(5000)) { logMessage("发送超时: " + socket->errorString()); return; } bytesSent += written; // 更新进度条 int progress = static_cast<int>((bytesSent * 100) / totalSize); ui->progressBar->setValue(progress); logMessage(QString("已发送 %1/%2 字节").arg(bytesSent).arg(totalSize)); } qint64 elapsed = uploadTimer.elapsed(); logMessage(QString("文件上传完成 (%1 ms),等待服务器确认...").arg(elapsed)); // 设置等待确认状态 waitingForUploadConfirm = true; // 设置超时检测 QTimer::singleShot(10000, this, [this]() { if (waitingForUploadConfirm) { logMessage("错误:等待服务器确认超时"); waitingForUploadConfirm = false; } }); } void MainWindow::socketConnected() { logMessage("已连接到服务器"); ui->statusLabel->setText("已连接"); ui->connectButton->setText("断开连接"); setUIEnabled(true); } void MainWindow::socketDisconnected() { logMessage("与服务器断开连接"); ui->statusLabel->setText("未连接"); ui->connectButton->setText("连接"); setUIEnabled(false); resetTransfer(); } void MainWindow::socketReadyRead() { if (isReceivingFile) { // 接收文件数据 QByteArray data = socket->readAll(); bytesReceived += data.size(); fileData.append(data); // 更新进度条 if (fileSize > 0) { int progress = static_cast<int>((bytesReceived * 100) / fileSize); ui->progressBar->setValue(progress); logMessage(QString("已接收 %1/%2 字节").arg(bytesReceived).arg(fileSize)); } // 检查是否接收完成(根据文件大小判断) if (bytesReceived >= fileSize && fileSize > 0) { isReceivingFile = false; // 保存文件 QString savePath = QFileDialog::getSaveFileName(this, "保存文件", "linelaser.bin", "二进制文件 (*.bin)"); if (!savePath.isEmpty()) { QFile file(savePath); if (file.open(QIODevice::WriteOnly)) { file.write(fileData); file.close(); logMessage(QString("文件保存成功 (%1 字节)").arg(fileData.size())); } else { logMessage("错误:无法保存文件 - " + file.errorString()); } } else { logMessage("下载完成但未保存文件"); } resetTransfer(); } return; } // 读取服务器响应 QByteArray response = socket->readAll(); QString responseStr = QString::fromUtf8(response); logMessage("服务器响应: " + responseStr); if (responseStr.startsWith("ERROR:")) { logMessage("服务器错误: " + responseStr.mid(6)); } else if (responseStr.startsWith("OK:")) { if (waitingForUploadConfirm) { logMessage("上传成功: " + responseStr.mid(3)); waitingForUploadConfirm = false; } else { logMessage("操作成功: " + responseStr.mid(3)); } } else if (responseStr.startsWith("SIZE:")) { // 收到文件大小信息 QString sizeStr = responseStr.mid(5); fileSize = sizeStr.toLongLong(); bytesReceived = 0; fileData.clear(); logMessage(QString("文件大小: %1 字节").arg(fileSize)); ui->progressBar->setMaximum(100); } else { logMessage("未知响应: " + responseStr); } } void MainWindow::socketError(QAbstractSocket::SocketError error) { Q_UNUSED(error) logMessage("网络错误: " + socket->errorString()); ui->statusLabel->setText("连接错误"); resetTransfer(); } void MainWindow::logMessage(const QString &message) { QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss"); ui->logText->append(QString("[%1] %2").arg(timestamp, message)); // 确保日志可见 QTextCursor cursor = ui->logText->textCursor(); cursor.movePosition(QTextCursor::End); ui->logText->setTextCursor(cursor); // 实时刷新界面 qApp->processEvents(); } void MainWindow::setUIEnabled(bool enabled) { ui->downloadButton->setEnabled(enabled); ui->uploadButton->setEnabled(enabled); ui->ipEdit->setEnabled(!enabled); ui->portEdit->setEnabled(!enabled); } void MainWindow::resetTransfer() { isReceivingFile = false; waitingForUploadConfirm = false; fileSize = 0; bytesReceived = 0; fileData.clear(); ui->progressBar->setValue(0); }这是qt客户端cpp文件 会遇到qt程序发送文件完成但是服务器确认超时问题,给出修改方案和代码
最新发布
05-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值