当前位置: 首页 > 知识库问答 >
问题:

套接字-消息只发送一次

宰父子安
2023-03-14

我正在尝试创建一个仅在共享内存中具有文件的小文件服务器。客户端应该向服务器发送命令,如 CREATE、DELETE 等。但是,我还没有到那个阶段。

我已经准备了一个服务器和一个客户端。服务器接受套接字,并为每个客户端连接创建一个新线程(要求)。

当我启动客户端时,我可以成功连接到服务器并发送将被接收的消息。但是,这只能工作一次。发送我的命令后,服务器将不会收到任何其他命令。

我尝试使用换行符捕获消息,但这似乎不起作用,因此任何帮助将不胜感激。

先谢谢你。

服务器源:

/*
 * main.c - the server file. 
 *
 *  Created on: Apr 26, 2014
 *      Author: fish-guts
 */

#include "main.h"

/* our main server buffer */
char serverbuf[4096];
static int client_sock;

void launch_app(char *argv[]) {
    if ((strcmp(argv[1], "start")) == 0)
        startup();
    else if ((strcmp(argv[1], "stop")) == 0)
        stop_server();
    else {
        fprintf(stderr,
                "Invalid Command: %s. Valid Commands are     ./fileserver         [start|stop]\n",
                argv[1]);
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char* argv[]) {
if (argc > 1)
    launch_app(argv);
    else {
        fprintf(stderr,
                "No argument supplied. Valid Argument are [start|stop]\n");
        exit(EXIT_SUCCESS);
    }
    return 0;
}

void print_start_msg(void) {
    fprintf(stderr, "###############################\n");
    fprintf(stderr, "Welcome to Severin'ŝ FileServer\n");
    fprintf(stderr, "###############################\n");
}

void stop_server(void) {
    exit(EXIT_SUCCESS);
}

void startup(void) {
    print_start_msg();
    start_server();
}

void start_server(void) {
    int s, len, rc;
    int tid;
    long t;
    char buf[100000];

    struct sockaddr_in addr;
    struct sockaddr_in client;
    pthread_t client_thread;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    unsigned short port = PORT;

    // clear the struct
    memset((char*) &addr, 0, sizeof(addr));

    fprintf(stderr, "\n\nStarting server...");
    // let's set some values

    /* type of socket created in socket() */
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons((unsigned short) PORT);

    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
    /* bind the socket to the port specified above */

    if ((rc = bind(sock, (struct sockaddr *) &addr, sizeof(addr))) < 0) {
    fprintf(stderr, "Error binding address: %s\n", strerror(errno));
    exit(EXIT_FAILURE);
} else {
    fprintf(stderr, "Bind Successful\n");
}

if ((listen(sock, serverbuf) < 0)) {
    fprintf(stderr, "Listen Failed\n");
    exit(EXIT_FAILURE);
} else {
    fprintf(stderr, "Server started successfully, listening on port %d\n",
            PORT);
}
// the  main server loop
while (!quitting) {
    len = sizeof(struct sockaddr_in);

    client_sock = accept(sock, (struct sockaddr *) &client, &len);
    if (client_sock < 0) {
        fprintf(stderr, "Accept failed\n");
    } else {
        /* This is the client process */
        tid = pthread_create(&client_thread, NULL, doprocessing, (void*) t);
        if (tid) {
            fprintf(stderr, "Error creating thread: %d\n", tid);
        }
    }
}
}

void *doprocessing(void) {
    int n;

    int s, len, rc;
    char buf[100000];
    char *str = "Welcome\n";

    bzero(buf, 100000);
    n = write(client_sock, str, sizeof(str));

    if (n < 0) {
        fprintf(stderr, "ERROR writing to socket");
        exit(1);
    }
    s = recv(client_sock, buf, sizeof(serverbuf),0);
    if (s) {
        fprintf(stderr,"Bytes received: %i\n",s);
        buf[s] = 0;
        // we use CRLF as a line breaker, its easier to parse the commands
        char *pch = strtok(buf, "\n");
        while (pch != NULL) {
            strcpy(serverbuf, pch);
            fprintf(stderr, "Command: %s\n", buf);
            //TODO: add command handler
            //parse();
            serverbuf[s] = 0;
            pch = strtok(NULL, "\r\n");
            //addlog(1, serverbuf);
        }
    } else {
        fprintf(stderr,"No data received\n");
    }
}

客户来源:

#include "main.h"

#define PORT 8083
#define BUF_SIZE 1024

int quitting;

void start_client(const char *address);

int main(int argc, char* argv[]) {
    start_client("localhost");
}

void start_client(const char *address) {
    struct hostent *he;
    struct sockaddr_in server;
    int s;
    char buf[BUF_SIZE];
    char input[BUF_SIZE];
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if ((he = gethostbyname(address)) == NULL) {
        fprintf(stderr,"error resolving hostname..");
        exit(1);
    }

    /*
     * copy the network address part of the structure to the
     * sockaddr_in structure which is passed to connect()
     */
    memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
    server.sin_family = AF_INET;
    server.sin_port = htons((unsigned short)PORT);

    /* connect */
    if (connect(sock, (struct sockaddr *) &server, sizeof(server))) {
        puts("Error connecting...\n");
        exit(1);
    } else {
        fprintf(stderr,"Connected to server\n");
    }
    send(sock,"Hallo Server\n",BUF_SIZE,0);
    int i = 0;
    while(!quitting) {

        if(i<100) {
            send(sock,"yallo",6,0);
        }
        fgets(input, sizeof(input), stdin);
        if(send(sock,input,strlen(input),0)<0) {
            fprintf(stderr,"Error sending data: %s\n",errno);
        } else {
            fprintf(stderr,"Hello: %s\n",buf);
        }
        s = recv(sock, buf, sizeof(buf),0);
        if(s) {
            buf[s] = 0;
            fprintf(stderr,"Message from Server: %s\n",buf);
        } else {
            fprintf(stderr,"Error in recv: %s\n",errno);
        }
    }
}

共有1个答案

韦高格
2023-03-14

处理套接字时的一些规则:

1.低级消息字段定义

永远记住,您正在读取和写入字节流。您必须记住的另一条规则是,TCP/IP并不保证一次写入等于一次读取。但是,这些规则对您意味着什么?

上述规则的组合意味着在从套接字读取之前,您必须确切地知道需要读取多少字节。所以:

  1. (情况 1):如果您正在读取固定长度的字段,则从流中读取的字节数与该字段中的字节数一样多。
  2. (情况 2):如果您想读取可变长度字段,请在发送之前预先附加该字段的字节计数。

(情况 1):例如,如果您要发送/接收一个四字节整数,那么他的代码应如下所示:

int myInt = 12345;
send(sockfd, &myInt, sizeof(myInt), my_flags);

在读取端:

int myReceivedInt = 0;
int received_count = recv(client_sock, &myReceivedInt, sizeof(myReceivedInt), 0);

注意:不同的平台为数据类型定义了不同的大小。

(案例2):

如果您要发送可变长度字段(例如字符串),则代码应如下所示:

// get string length
int str_size = strlen(my_str); 
// send fixed-length data to pre-pend variable-length field with the latter's size
send(sockfd, &str_size, sizeof(str_size), my_flags);  
// send the variable-length field.
send(sockfd, my_str, str_size, my_flags); 

在接收端:

int string_size = 0;
char *received_string = NULL;
int received_count = recv(client_sock, &string_size, sizeof(string_size), 0);
/*
  now we know how big the string is, so we allocate 
  memory for it or use a previously allocated buffer. This is omitted...
*/
int received_count = recv(client_sock, &received_string, string_size, 0);
received_string[string_size] = '\0';

上述两条规则的另一个后果是,您可能需要从套接字读取多次才能接收一条完整的消息。对于较大的消息(例如文件传输)尤其如此。因此,当接收到大消息时,将recv()保持循环并继续读取,直到您阅读了整个消息。

例如,如果您正在读取文件:

int file_size = 0, read_so_far = 0, ret = 0;
recv(sockfd, &file_size , sizeof(file_size), 0); 
// now we know how big is the file...allocate buffer (file_content) and read file fully
while(read_so_far != file_size)
{
   ret = recv(sockfd, file_content + read_so_far, file_size - read_so_far, 0); 
   if(ret < 0)
   {
      // handle error case, socket reset maybe?
      perror("Something bad happened with the socket");
      break;
   }
   read_so_far += ret;   
}

2.定义协议

现在您已经了解了低级内容,您可以定义协议。该协议是对要通过连接发送的消息格式的描述。每条消息都由一个或多个字段组成,如上所示。

3. 定义消息

请记住,每条消息都应定义一个命令和命令的参数。例如,删除文件的消息应包含命令“删除”和“文件名”。因此,删除文件消息可以定义为:

  • 1字节用于命令4字节用于name_size变长file_name。

对其他消息做同样的事情,您最终会得到协议。代码可能如下所示:

char command = 0;
s = recv(client_sock, &command, 1,0); 

if(command == LIST_NODES_COMMAND)
{
 // read arguments for command and process it.
} else if(command == CREATE_FOLDER_COMMAND)
{
 // read arguments for command and process it.
}

4. 消息处理循环

服务器的最后一步是将消息处理放在循环中:

while(1)
{
    char command = 0;
    s = recv(client_sock, &command, 1,0);
    if(command == LIST_NODES_COMMAND)
    {
       // handle list command here.
    } else if(command == CREATE_FOLDER_COMMAND)
    {
       // read params like above and handle command.

    } else if (command == COMMAND_QUIT)
    {
       // do the stuff necessary before the client disconnects.
    }
}
 类似资料:
  • 我最近开始学习php套接字。我想在服务器和客户端之间创建一个永久传输控制协议!但是我的PHP套接字客户端只发送和接收一条消息。我想无限期地发送和接收消息,通过一个连接。 服务器php: 客户php:

  • 我一直在开发一个简单的python套接字聊天室,客户端和服务器可以在其中相互发送消息。我遇到的问题是服务器和客户端一次只能发送一条消息。我希望它能像任何其他聊天室一样工作,在那里我可以在发送消息时收到消息,任何帮助都会有很大帮助

  • 如果我不关闭套接字: 客户端没有输出。服务器从不发送消息,或者客户端从不接收消息。 有人知道发生这种情况的可能原因吗?客户端使用一个线程接收消息,一个线程发送消息。客户端套接字在主客户端线程中创建,因此接收者和发送者线程使用当前套接字进行通信。 提前谢了。

  • 我在询问之前搜索了这个问题,但我找不到类似的东西。我开发了一个客户端/服务器解决方案来发送/接收HL7消息。我使用套接字将客户端连接到服务器,从这个连接中,我只能使用OutputSteam对象发送1条HL7消息。我如何在同一个套接字连接中发送多个HL7?我尝试了不同的方法,但它们不能正常工作。 以下是我的客户端代码: 从服务器端 如何在同一套接字连接中发送更多HL7消息?

  • 现在我有了下面的类,它使用OkHttp处理WebSockets,但它的设置方式是,我无法保持WebSocket打开,以便向web Socket发送新消息。如何在不创建新的websocket连接的情况下继续向同一个打开的websocket发送消息?

  • 我有一个关于如何改进这个代码的问题。我是Socket.io的新手,我一直在YouTube上看一个关于私人信息的视频教程。我只是想知道有没有更好的方法来改进下面的代码行? 但似乎不起作用。