java - 与发送文件的选项聊天
问题描述
我想创建一个带有发送文件选项的简单 TCP 聊天。
向其他客户端发送消息有效,但发送文件无效。我的聊天只发送几个字节的文件。
聊天的工作原理是:服务器启动并等待客户端,客户端连接到服务器,他们可以通过服务器向其他客户端发送消息。我想允许与文件相同。
服务器是用 C 编写的,客户端是用 Java 编写的(我有这样的指导方针)。
服务器:
for (i = 0; i < max_clients; i++) {
sd = client_socket[i];
memset(buffer, 0, 10000);
if (FD_ISSET( sd , &readfds)) {
if ((valread = read( sd , buffer, 1024)) == 0) {
getpeername(sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
printf("Host disconnected , ip %s , port %d \n" ,
inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
close( sd );
client_socket[i] = 0;
}
else {
// When message "start" arrived download the file and send it back to other clients
if (strcmp(buffer, "start") == 0) {
uint8_t buff[10000];
// Read chunks of file
while (read( sd , buff, sizeof(buff)) > 0) {
// Sending chunks of file to other clients
for(j=0; j<max_clients; j++) {
int outSock = client_socket[j];
if(outSock != master_socket && outSock != sd) {
send(outSock , buff , sizeof(buff) , 0 );
}
}
}
} else {
buffer[valread] = '\0';
for(j=0; j<max_clients; j++) {
int outSock = client_socket[j];
if(outSock != master_socket && outSock != sd) {
send(outSock , buffer , strlen(buffer) , 0 );
}
}
}
}
}
}
客户:
@FXML
void sendFile(ActionEvent event) {
FileChooser fileChooser = new FileChooser();
File file = fileChooser.showOpenDialog(null);
// Send "start" message to let server know that I'm going to send a file
out.println("start");
out.flush();
try {
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
//Get socket's output stream
OutputStream os = clientSocket.getOutputStream();
//Read File Contents into contents array
byte[] contents;
long fileLength = file.length();
long current = 0;
while(current!=fileLength){
int size = 10000;
if(fileLength - current >= size)
current += size;
else{
size = (int)(fileLength - current);
current = fileLength;
}
contents = new byte[size];
bis.read(contents, 0, size);
os.write(contents);
System.out.print("Sending file ... "+(current*100)/fileLength+"% complete!");
}
os.flush();
System.out.println("File sent successfully!");
} catch(Exception e) {
}
}
public ChatWindowController() {
try {
clientSocket = new Socket("127.0.0.1", 54000);
outToServer = new DataOutputStream(clientSocket.getOutputStream());
inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream(), true);
thread = new Thread() {
@Override
public void run() {
try {
while(isRunning) {
if (ta_display != null) {
String message = inFromServer.readLine();
if (!isDownloadingFile) {
System.out.println(message);
ta_display.appendText(message + '\n');
if (message.equals("start")) {
isDownloadingFile = true;
}
} else {
byte[] contents = new byte[10000];
//Initialize the FileOutputStream to the output file's full path.
FileOutputStream fos = new FileOutputStream("/example/test.png");
BufferedOutputStream bos = new BufferedOutputStream(fos);
InputStream is = clientSocket.getInputStream();
//No of bytes read in one read() call
int bytesRead = 0;
while((bytesRead=is.read(contents))!=-1)
bos.write(contents, 0, bytesRead);
bos.flush();
System.out.println("File saved successfully!");
}
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
};
thread.start();
} catch(Exception e) {
e.printStackTrace();
}
}
当我点击按钮 sendFile 方法被调用,然后我选择文件,我想发送这个文件,并与其他客户端下载它。
解决方案
好的,服务器在接收文件时有一个bug:
//client sends 10000 bytes of data in chunks of unknown size
//receive one chunk of unknown size
while (read( sd , buff, sizeof(buff)) > 0) {
for(j=0; j<max_clients; j++) {
int outSock = client_socket[j];
if(outSock != master_socket && outSock != sd) {
// Send 10000 bytes aka the received chunk and whatever else is in the buffer to all clients
send(outSock , buff , sizeof(buff) , 0 );
}
}
您需要先接收所有数据,然后将其发送给客户端,或者仅发送块大小的多个字节,例如在客户端的文件写入器中或如下所示:
int chunk = 0;
while ((chunk = read( sd , buff, sizeof(buff))) > 0) {
...
send(outSock , buff , chunk , 0);
另外我不确定客户端文件编写代码是否有效,您应该确保没有意外将以下消息写入文件。
推荐阅读
- android - 是否有 android:onClick="abc",但用于按住?
- javascript - 如何在 for 循环中运行 $.getJSON 并在所有调用完成后执行代码?
- c# - C# Entity Framework Core 编写没有模型的原始 SQL
- python - Python rpg boss无限攻击循环
- swift - 对象不受中心 y 锚的底部锚的约束
- python - __init__.py 的简单案例和未找到神秘模块的导入
- java - 从字符串中挑选出正确的字符;
- python - 如何基于一个公共列但不同的内容合并/扩展两个 python pandas 数据框?
- r - 在 R 中编写 curl 请求
- node.js - 如何在 NodeJS 中使用 RSA OAEP SHA-256 进行加密