【发布时间】:2021-04-12 04:55:40
【问题描述】:
我正在编写一个客户端-服务器解决方案。目标是远程连接到摄像头并在客户端接收其摄像头馈送。我为此使用 TCP 协议。是的,这是我第一次这样做。
客户端是用 Java 编写的。这意味着它使用 BufferedReader.readLine()。是的,问题是它导致 null 并且我知道这意味着服务器关闭了连接。我也得到了一位资深程序员的确认。
服务器是用 C 语言编写的。我的问题是我找不到一个明显的原因来解释为什么它的代码会导致立即关闭连接。我找到了多个支持我已经使用的方法的代码示例。
我想知道:
- 在这种情况下,导致 null 的 BufferedReader 能否立即指示其他内容?
- 我的服务器代码中遗漏了什么,为什么它会关闭连接?
这是我的原始服务器代码(请参阅下面的更新):
#define _GNU_SOURCE -D
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <syslog.h>
#include <capture.h>
#include <sys/sendfile.h>
#include <pthread.h>
//define where the network camera is located
#define PORT_NUMBER 80
#define SERVER_ADDRESS "localhost"
void *msg_handler(void *);
//step 1
/* The server is supposed to send a list of available resolutions and frames per second, so the client can choose*/
//we create a handler to each client
void *client_handler(int server_socket)
{
int serverSocket = *(int *)server_socket; // this is a pointer to the server socket
char *message, cli_message[40000];
message = capture_get_resolutions_list(0); //list of available image resolutions;
write(serverSocket, message, strlen(message)); //send to client the list of resolutions
write(serverSocket, "\n", strlen("\n")); //breaking line so the client can see the list
memset(message, 0, strlen(message)); //resetting the message variable
//variables related to the image
media_stream *imageStream;
recv(serverSocket, cli_message, 40000, 0); // receive information from client regarding resolution
media_frame *imageFrame;
void *data;
size_t imageSize;
int arrayRow = 0; //related to the data array - we need it if we want to loop later through the array
//opening the stream in order to capture image
imageStream = capture_open_stream(IMAGE_JPEG, cli_message);
//send image to client with chosen resolution
while (1) //condition always true, i.e. infinite loop
{
imageFrame = capture_get_frame(imageStream);
data = capture_frame_data(imageFrame);
imageSize = capture_frame_size(imageFrame);
sprintf(message, "%zu\n", imageSize); //converting the size to a char , and send to client
write(serverSocket, message, strlen(message)); //sending the size to the client
unsigned char arrayRow_data[imageSize];
for (arrayRow = 0; arrayRow < imageSize; arrayRow++)
{
arrayRow_data[arrayRow] = ((unsigned char *)data)[arrayRow];
}
//try to send data to client ---> see the possible errors
int sendDataError = write(serverSocket, arrayRow_data, sizeof(arrayRow_data));
if (sendDataError < 0)
{
syslog(LOG_INFO, "oops, connection with client lost");
break;
}
//restoring variables to ZERO
memset(data, 0, sizeof(data));
memset(arrayRow_data, 0, sizeof(arrayRow_data));
capture_frame_free(imageFrame);
}
syslog(LOG_INFO, "ERROR:CAN NOT SEND IMAGE");
capture_close_stream(imageStream);
close(serverSocket);
return 0;
}
//main function
//creating a socket
int main(void)
{
//char server_message[256]= "Congrats, you have reached the server";
int server_socket;
int client_socket;
int *new_socket;
int conn;
struct sockaddr_in server, client;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(1025);
//bind the socket to the specified IP and port
bind(server_socket, (struct sockaddr *)&server, sizeof(server));
conn = sizeof(struct sockaddr_in);
listen(server_socket, 5);
//accept the connection from the client and send the message
while ((client_socket = accept(server_socket, (struct sockaddr *)&client, (socklen_t *)&conn)))
{
pthread_t client_thread;
new_socket = malloc(sizeof *new_socket);
new_socket = client_socket;
//establishing a message thread and possible errors
if (pthread_create(&client_thread, NULL, client_handler, (void *)new_socket) < 0)
{
syslog(LOG_INFO, "ERROR. MESSAGE THREAD NOT ESTABLISHED");
return 1;
}
}
if (client_socket < 0)
{
syslog(LOG_INFO, "Sorry, client socket declined");
free(new_socket);
return 1;
}
return 0;
}
这是一个更新的服务器代码。它有很多 cmets,因为我们想跟踪我们现在所做的事情。还是同样的问题:
#define _GNU_SOURCE -D
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <syslog.h>
#include <capture.h>
#include <sys/sendfile.h>
#include <pthread.h>
//define where the network camera is located
#define PORT_NUMBER 443
#define SERVER_ADDRESS "localhost"
//store recieved messages from client
//char cli_message;
//This is a buffer to store a welcome message to client in.
char buffer[1024];
char *message; //declared like this instead.
void * client_handler(void *server_socket) //This probably had the wrong input variable.
{
//int serverSocket = (int) (intptr_t)server_socket;
int new_socket = (int)(intptr_t) (server_socket); // this is a pointer to the server socket.
//char *message, cli_message[2000];
//HERE: get the media_stream BEFORE trying to fetch message
media_stream *imageStream;
media_frame *imageFrame;
//ADDED: storing the image data.
char *imageData;
//ADDED: storing the size of the image data array.
size_t imgSize;
//MOVED and changed from server_socket
recv(new_socket, message, 2000, 0); // receive information from client regarding resolution
//MOVED. NOW we get the available resolutions.
message = capture_get_resolutions_list(0); //list of available image resolutions;
//ADDED: Fetching the length of message in an int to make CERTAIN it's right and using a different method later in write to send it.
int first_message_size = strlen(message);
//write(serverSocket, message, strlen(message));
write(new_socket, message, first_message_size); //send to client the list of resolutions
write(new_socket, "\n", strlen("\n")); //breaking line so the client can see the list
memset(message, 0, strlen(message)); //resetting the message variable
//Store the recieved resolution and FPS from client picked by user.
recv(new_socket, message, 2000, 0);
//opening the stream in order to capture image
imageStream = capture_open_stream(IMAGE_JPEG, message);
//send image to client with chosen resolution
while (1) //condition always true, i.e. infinite loop
{
imageFrame = capture_get_frame(imageStream);
imageData = capture_frame_data(imageFrame);
imgSize = capture_frame_size(imageFrame);
sprintf(message, "%zu\n", imgSize); //converting the size to a char , and send to client
write(new_socket, message, strlen(message)); //sending the size to the client
//EDITED: sends image data to client, we also check if this write fails. We send imgSize, not sizeof(data).
int sending = write(new_socket, imageData, imgSize);
//Here we check that it's sending. If it isn't, the problem might be
//that the CLIENT disconnected.
if (sending < 0)
{
syslog(LOG_INFO, "ERROR! Failed to send. Client disconnected?");
break;
}
//EDITED: Another reset of variables cause we DO NOT want to store this.
memset(imageData, 0, sizeof(imageData));
//memset(arrayRow_data, 0, sizeof(arrayRow_data));
capture_frame_free(imageFrame);
//unsigned char arrayRow_data[imgSize];
int arrayRow;
for (arrayRow = 0; arrayRow < imgSize; arrayRow++)
{
imageData[imgSize] = imageData[arrayRow];
}
}//End of while
syslog(LOG_INFO, "Exiting...");
capture_close_stream(imageStream);
close(new_socket);
//ADDED
pthread_exit(NULL);
return 0;
}
int start(void)
{
//(might solve our log issues)
openlog ("server", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
//message onto server log
syslog(LOG_INFO, "Server running...");
//char server_message[256]= "Congrats, you have reached the server";
int server_socket;
int new_socket;
//int *new_socket;
//int conn; //wat is dis?
//struct sockaddr_in server, client;
struct sockaddr_in server;
struct sockaddr_storage storage; //This is new. Testing.
socklen_t addressLength;
server_socket = socket(PF_INET, SOCK_STREAM, 0); //Changed to PF
server.sin_family = AF_INET;
server.sin_port = htons(443);
server.sin_addr.s_addr = htonl(INADDR_ANY);//we added htons & htonl
//Deal with eventual padding field
memset(server.sin_zero, '\0', sizeof server.sin_zero);
//bind the socket to the specified IP and port
bind(server_socket, (struct sockaddr *) &server, sizeof(server));
//conn = sizeof(struct sockaddr_in); ----> wat is?
//Gave the listener a few more helpers. Longer time and max 40 con
//requests.
if(listen(server_socket, 50)==0)
{
syslog(LOG_INFO, "Listening\n");
}
else
{
syslog(LOG_INFO, "Not listening, lalalala.\n");
}
pthread_t client_thread[60]; //Move to outside of loop and given size.
int i = 0; //used in the if-statement in the below while-loop.
int end = 1; //used to end the while-loop.
//accept the connection from the client and send the message
while(end)
{
addressLength = sizeof storage;
//new_socket = malloc(sizeof *new_socket);
//new_socket = accept(server_socket, (struct sockaddr *) &client, (socklen_t *)&conn);
*new_socket = accept(server_socket, (struct sockaddr *) &storage, &addressLength); //New addition
//establishing a message thread and possible errors
//if(pthread_create(&client_thread, NULL, client_handler, (void *)((intptr_t) new_socket))< 0)
//Trying this if-statement instead..
if(pthread_create(&client_thread[i], NULL,client_handler,(void *)((intptr_t) new_socket)) != 0)
{
syslog(LOG_INFO, "ERROR. MESSAGE THREAD NOT ESTABLISHED!");
}
i++;
if( i > 50)
{
end = 0;
}
}
return 0;
}
//结束
【问题讨论】:
-
什么是捕获库(
capture.h)? -
recv(serverSocket, cli_message, 40000, 0)?你怎么知道这条消息实际上有多长?你没有把它当作一个字符串,是吗?recv()not 是否正确终止字符串。 -
@Ôrel 我使用的是 Axis 相机,它来自他们的库。
-
这个
new_socket =accept(...应该是*new_socket =accept(....。此外,代码仍然不会检查对write()和recv()的调用是否有错误。 -
@alk 从那以后,我们已经彻底检查了代码,并基于新的发现和反馈。由于我们仍然遇到问题,我们接下来将研究客户端。但是新的服务器代码作为更新版本在这篇文章中,仍然感谢反馈。我正在研究检查 recv(),只是想找到一个关于如何最好地做到这一点的好资源。
标签: c tcp client-server