Ich bin neu in der C-Socket-Programmierung. Ich habe gelernt, dass nach dem Senden von Daten über einen Socket, sollte entweder close
oder shutdown
der Socket-Deskriptor, der ein EOF an das andere Ende gesendet wird ausgelöst. Ohne EOF blockiert read
/recv
weiterhin.In C, wie EOF nach dem Schreiben von Daten ohne Close/Shutdown eine Steckdose angeben?
Jetzt frage ich mich, ob es überhaupt möglich ist, den Sockel für weitere Datenübertragung offen zu halten. Was ich gelesen habe, scheint das zu sein, was Leute tun, wenn sie eine Keep-Alive-HTTP-Verbindung aufbauen. Aber ich konnte nicht herausfinden, wie das erreicht wird.
Der folgende Code zeigt das Szenario, das nach dem write
des Clients festhängt.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define thread_printf(...) printf("[PID %d] ", getpid()); printf(__VA_ARGS__);
int create_socket()
{
int socket_fd;
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd < 0)
perror("Error opening socket");
return socket_fd;
}
int listen_port(int portno)
{
int socket_fd;
struct sockaddr_in server_addr;
socket_fd = create_socket();
memset((void *) &server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portno);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
perror("Error on binding");
listen(socket_fd, 5);
return socket_fd;
}
int connect_port(const char *host, int portno)
{
int socket_fd;
struct sockaddr_in proxy_addr;
struct hostent *proxy;
socket_fd = create_socket();
proxy = gethostbyname(host);
if (proxy == NULL)
perror("Error no such host");
proxy_addr.sin_family = AF_INET;
proxy_addr.sin_port = htons(portno);
memcpy((void *) &proxy_addr.sin_addr.s_addr, (void *) proxy->h_addr, proxy->h_length);
if (connect(socket_fd, (struct sockaddr *) &proxy_addr, sizeof(proxy_addr)) < 0)
perror("Error connecting");
return socket_fd;
}
int read_socket(int fd, char *buf, int bufsize)
{
thread_printf("read_socket\n");
int m = 0; /* total number of bytes received */
int n = 0; /* number of bytes received in a single read */
while (m < bufsize - 1)
{
n = read(fd, buf + m, bufsize - m - 1);
if (n == -1) /* socket read error */
{
perror("Error reading socket");
break;
}
if (n == 0) /* socket is closed */
break;
m += n;
}
if (m >= bufsize)
perror("Error buffer overflow");
buf[m] = '\0';
return m;
}
int write_socket(int fd, char *buf, int len)
{
thread_printf("write_socket\n");
int m = 0;
int n = 0;
while (m < len)
{
n = write(fd, buf + m, len - m);
if (n == -1) {
perror("Error socket send");
break;
}
if (n == 0) /* socket is closed */
break;
m += n;
}
thread_printf("m = %d, len = %d\n", m, len);
return m;
}
int main()
{
int socket_fd = listen_port(3129);
if (fork() == 0) /* client */
{
close(socket_fd);
int client_socket_fd = connect_port("127.0.0.1", 3129);
int n;
char *msg = "This is a client request\n";
char buf[1024];
memset((void *) buf, 0, 1024);
n = write_socket(client_socket_fd, msg, strlen(msg));
thread_printf("client sent: %s\n", msg);
n = read_socket(client_socket_fd, buf, 1024);
thread_printf("client received: %s\n", buf);
close(client_socket_fd);
}
else /* server */
{
struct sockaddr_in client_addr;
int client_len;
client_len = sizeof(client_addr);
int client_socket_fd = accept(socket_fd, (struct sockaddr *) &client_addr, &client_len);
int n;
char *msg = "This is a server response\n";
char buf[1024];
n = read_socket(client_socket_fd, buf, 1024);
thread_printf("server received: %s\n", buf);
n = write_socket(client_socket_fd, msg, strlen(msg));
thread_printf("server sent: %s\n", msg);
close(client_socket_fd);
}
return 0;
}
HTTP Keep-Alive erfordert 'Inhalt -Länge 'Header oder Chunks, damit Sie wissen, wo die nächste Antwort beginnt. – Ryan
Danke für das Aufzeigen @Ryan. Es macht für mich mehr Sinn nach dem Lesen, was 'Content-Length' und' Transfer-Encoding' bedeuten und wie sie verwendet werden. – Yan
OT: Der letzte Parameter, der an 'accept()' übergeben wird, muss die Adresse eines 'socklen_t' sein, nicht eines' int'. Auch 'read()' und 'write()' geben 'ssize_t' not' int' zurück. – alk