我正在构建自己的网络服务器。现在,我的极简主义代码是:
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#define SERVER_PORT 80
int main () {
int nReqSocketId, nReqSize = 1024, nMainSocketId = socket(AF_INET, SOCK_STREAM, 0);
char *sRequest = malloc(nReqSize);
socklen_t nAddrLen;
struct sockaddr_in oAddress;
oAddress.sin_family = AF_INET;
oAddress.sin_addr.s_addr = INADDR_ANY;
oAddress.sin_port = htons(SERVER_PORT);
if (nMainSocketId == 0) {
fprintf(stderr, "Error during the creation of the socket\n");
return 1;
}
if (bind(nMainSocketId, (struct sockaddr *) &oAddress, sizeof(oAddress))) {
fprintf(stderr, "The port %d is busy\n", SERVER_PORT);
close(nMainSocketId);
return 1;
}
printf("HTTP server listening on port %d\n", SERVER_PORT);
while (1) {
if (listen(nMainSocketId, 10) < 0) {
perror("server: listen");
close(nMainSocketId);
exit(1);
}
nReqSocketId = accept(nMainSocketId, (struct sockaddr *) &oAddress, &nAddrLen);
if (nReqSocketId < 0) {
perror("server: accept");
close(nMainSocketId);
exit(1);
}
recv(nReqSocketId, sRequest, nReqSize, 0);
if (nReqSocketId > 0){
printf("The Client is connected...\n\n%s\n", sRequest);
}
write(nReqSocketId, "HTTP/1.1 200 OK\n", 16);
write(nReqSocketId, "Content-length: 50\n", 19);
write(nReqSocketId, "Content-Type: text/html\n\n", 25);
write(nReqSocketId, "<html><body><h1>Hello world!!!</h1></body></html>\n", 50);
close(nReqSocketId);
}
printf("Goodbye!\n");
close(nMainSocketId);
return 0;
}
我可以创建一个“软关闭机制”,让网络服务器打印“再见!”短语位于无限循环之后?当我输入“q”字母时,例如......
答案 0 :(得分:1)
为什么不消除所有这些写功能,只使用一个send()?
您需要做的就是将您的响应存储在缓冲区中,然后发送缓冲区:
// Global
#define MAX 2048
char response[MAX]; // No need for char*
// In Main
memset(response, 0, MAX); // **EDIT**
strcpy(response, "HTTP/1.1 200 OK\n");
strcat(response, "Content-length: 50\n");
strcat(response, "Content-Type: text/html\n\n");
strcat(response, "<html><body><h1>Hello world!!!</h1></body></html>\n");
// Now simply send the whole response in one go:
send(nReqSocketId, response, strlen(response), 0);
此外,您还可以简单地将其设为非持久连接,如下所示:
// Global
#define MAX 2048
char response[MAX]; // No need for char*
// In Main
memset(response, 0, MAX); // **EDIT**
strcpy(response, "HTTP/1.1 200 OK\n");
strcat(response, "Content-Type: text/html\n\n");
strcat(response, "<html><body><h1>Hello world!!!</h1></body></html>\n");
// Now simply send the whole response in one go again:
send(nReqSocketId, response, strlen(response), 0);
// Shutdown the socket so it cannot write anymore:
shutdown(nReqSocketId,1);
// Then totally close it when you are ready:
close(nReqSocketId);
后者可能更适合您目前正在做的事情;因为你没有在你的网络服务器中保持多个连接存活。
关闭服务器端的连接后,客户端(即Web浏览器)知道停止预期内容并将正确完成作业。
希望这有帮助。
干杯!
PS-
当然,这是对你在线程中的最后一个问题的回应,而不是软终止部分。
我还需要建议你memset(响应,0,MAX),以便每次响应时都有一个很好的清白。
答案 1 :(得分:0)
根据@ Medinoc,@ someuser和@Elchonon的建议,我通过以下方式纠正了我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <signal.h>
#define SERVER_PORT 80
int nStatus, nMainSocketId;
void onInt () {
printf("You have pressed CTRL-C.\n");
nStatus = 0;
shutdown(nMainSocketId, 2);
}
void onQuit () {
printf("You have pressed CTRL-\\.\n");
nStatus = 0;
shutdown(nMainSocketId, 2);
}
int main () {
int nReqSocketId, nReqSize = 1024;
nMainSocketId = socket(AF_INET, SOCK_STREAM, 0);
char *sRequest = malloc(nReqSize);
socklen_t nAddrLen;
struct sockaddr_in oAddress;
signal(SIGINT, onInt);
signal(SIGQUIT, onQuit);
oAddress.sin_family = AF_INET;
oAddress.sin_addr.s_addr = INADDR_ANY;
oAddress.sin_port = htons(SERVER_PORT);
nStatus = 1;
printf("\n W E L C O M E!\n\nPress CTRL-C or CTRL-\\ to quit.\n");
if (nMainSocketId == 0) {
fprintf(stderr, "Error during the creation of the socket\n");
return 1;
}
if (bind(nMainSocketId, (struct sockaddr *) &oAddress, sizeof(oAddress))) {
fprintf(stderr, "The port %d is busy\n", SERVER_PORT);
close(nMainSocketId);
return 1;
}
printf("HTTP server listening on port %d\n", SERVER_PORT);
while (nStatus) {
if (listen(nMainSocketId, 10) < 0) {
perror("server: listen");
close(nMainSocketId);
return 1;
}
nReqSocketId = accept(nMainSocketId, (struct sockaddr *) &oAddress, &nAddrLen);
if (nReqSocketId < 0) {
perror("server: accept");
close(nMainSocketId);
return 1;
}
recv(nReqSocketId, sRequest, nReqSize, 0);
if (nReqSocketId > 0){
printf("The Client is connected...\n\n%s\n", sRequest);
}
write(nReqSocketId, "HTTP/1.1 200 OK\n", 16);
write(nReqSocketId, "Content-length: 50\n", 19);
write(nReqSocketId, "Content-Type: text/html\n\n", 25);
write(nReqSocketId, "<html><body><h1>Hello world!!!</h1></body></html>\n", 50);
close(nReqSocketId);
}
printf("Goodbye!\n");
close(nMainSocketId);
return 0;
}
过程软终止,现在!!但是,不幸的是,插座没有! :( ...所以每当我按CTRL-C或CTRL- \以便退出时,我都会收到以下消息:
server: accept: Invalid argument
有任何修复建议吗?
答案 2 :(得分:0)
在* IXish OS下,一个信号将唤醒某些阻塞系统调用并让它们返回。
他们会指出一个错误并将errno
设置为EINTR
。您可以使用这些机制来优雅地关闭服务器。
请参阅下面稍作调整/更正的答案中的代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#define SERVER_PORT 8080 /* Using a port>1024 one does not need to run this as root. */
void onInt()
{
/* Do nothing. */
}
void onQuit()
{
/* Do nothing. */
}
int main()
{
int nMainSocketId = -1, nReqSocketId = -1;
size_t nReqSize = 1024;
char * sRequest = malloc(nReqSize); /* TODO: add error checking */
socklen_t nAddrLen;
struct sockaddr_in oAddress = {0};
signal(SIGINT, onInt); /* TODO: add error checking */
signal(SIGQUIT, onQuit); /* TODO: add error checking */
printf("\n W E L C O M E!\n\nPress CTRL-C or CTRL-\\ to quit.\n");
oAddress.sin_family = AF_INET;
oAddress.sin_addr.s_addr = INADDR_ANY;
oAddress.sin_port = htons(SERVER_PORT);
nMainSocketId = socket(AF_INET, SOCK_STREAM, 0);
if (nMainSocketId < 0) /* 0 is a valid file/socket descriptor! */
{
perror("server: socket() failed");
return 1;
}
if (bind(nMainSocketId, (struct sockaddr *) &oAddress, sizeof(oAddress)))
{
perror("server: bind() failed");
close(nMainSocketId);
return 1;
}
printf("HTTP server listening on port %d\n", SERVER_PORT);
while (1)
{
int result = 0;
if (listen(nMainSocketId, 10) < 0)
{
perror("server: listen() failed");
close(nMainSocketId);
return 1;
}
result = accept(nMainSocketId, (struct sockaddr *) &oAddress, &nAddrLen);
if (result < 0)
{
if (EINTR == errno)
{
printf("Shutdown requested. Exiting ...\n");
break;
}
perror("server: accept failed()");
close(nMainSocketId);
return 1;
}
nReqSocketId = result;
result = recv(nReqSocketId, sRequest, nReqSize, 0); /* TODO: Check wether the request size was really read! */
if (result < 0)
{
perror("server: recv() failed");
close(nMainSocketId);
return 1;
}
else if (result == 0)
{
printf("The client is disconnected. Waiting for another connection ...\n");
close(nReqSocketId);
nReqSocketId = -1;
continue;
}
printf("The client is connected...\n\n%s\n", sRequest);
/* TODO: add error checking for ALL write()s */
write(nReqSocketId, "HTTP/1.1 200 OK\n", 16);
write(nReqSocketId, "Content-length: 50\n", 19);
write(nReqSocketId, "Content-Type: text/html\n\n", 25);
write(nReqSocketId, "<html><body><h1>Hello world!!!</h1></body></html>\n", 50);
close(nReqSocketId);
nReqSocketId = -1;
}
printf("Goodbye!\n");
close(nMainSocketId);
return 0;
}
请注意,recv()
的调用也可能阻止等待数据,然后由于SIGINT
被中断。因此,在检查accept()
的结果时,应采用与recv()
相同的逻辑。
答案 3 :(得分:0)
@alk
感谢您的回答!你建议我另一种软终止我的网络服务器的方法......:
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <signal.h>
#define SERVER_PORT 80
int bExiting, nMainSocketId;
void terminateServer () {
if (bExiting) { return; }
printf("\n\nTerminating server...\n");
close(nMainSocketId);
bExiting = 1;
}
int main () {
int nRecvResult = -1, nReqSocketId = -1;
size_t nReqSize = 1024;
char * sRequest = malloc(nReqSize);
socklen_t nAddrLen;
struct sockaddr_in oAddress = {0};
printf("\n W E L C O M E!\n\nPress CTRL-C or CTRL-\\ to quit.\n");
signal(SIGINT, terminateServer);
signal(SIGQUIT, terminateServer);
oAddress.sin_family = AF_INET;
oAddress.sin_addr.s_addr = INADDR_ANY;
oAddress.sin_port = htons(SERVER_PORT);
nMainSocketId = socket(AF_INET, SOCK_STREAM, 0);
if (nMainSocketId < 0) {
perror("server: socket() failed");
return 1;
}
if (bind(nMainSocketId, (struct sockaddr *) &oAddress, sizeof(oAddress))) {
perror("server: bind() failed");
terminateServer();
return 1;
}
printf("HTTP server listening on port %d\n", SERVER_PORT);
while (bExiting == 0) {
if (listen(nMainSocketId, 10) < 0) {
perror("server: listen() failed");
terminateServer();
return 1;
}
nReqSocketId = accept(nMainSocketId, (struct sockaddr *) &oAddress, &nAddrLen);
if (bExiting) { break; }
if (nReqSocketId < 0) {
perror("server: accept failed()");
terminateServer();
return 1;
}
nRecvResult = recv(nReqSocketId, sRequest, nReqSize, 0);
if (nRecvResult < 0) {
perror("server: recv() failed");
terminateServer();
return 1;
}
if (nRecvResult == 0) {
printf("The client is disconnected. Waiting for another connection...\n");
close(nReqSocketId);
continue;
}
printf("The client is connected...\n\n%s\n", sRequest);
/* This is only a simple "Hello world"... */
write(nReqSocketId, "HTTP/1.1 200 OK\n", 16);
write(nReqSocketId, "Content-length: 50\n", 19);
write(nReqSocketId, "Content-Type: text/html\n\n", 25);
write(nReqSocketId, "<html><body><h1>Hello world!!!</h1></body></html>\n", 50);
close(nReqSocketId);
}
free(sRequest);
printf("Goodbye!\n");
return 0;
}
你怎么看?我应该添加一些东西吗?
不过,我还有一个问题。我可以将fprintf()与文件描述符结合使用作为write()的替代方法吗?而且......怎么样?
答案 4 :(得分:0)
@PandaSobao
我使用fprintf()结合文件描述符删除了write(),我认为这是最好的方法,因为不需要strlen()...看看下面的代码:
#include <stdio.h>
#include <netinet/in.h>
#include <signal.h>
#define REQUEST_SIZE 1024
#define SERVER_PORT 80
int bExiting, nMainSocketId;
void terminateServer () {
if (bExiting) { return; }
printf("\n\nTerminating server...\n");
close(nMainSocketId);
bExiting = 1;
}
int main () {
int nRecvResult = -1, nReqSocketId = -1;
char sRequest[REQUEST_SIZE];
socklen_t nAddrLen;
struct sockaddr_in oAddress;
FILE *nRespFD;
printf("\n W E L C O M E!\n\nPress CTRL-C or CTRL-\\ to quit.\n");
signal(SIGINT, terminateServer);
signal(SIGQUIT, terminateServer);
oAddress.sin_family = AF_INET;
oAddress.sin_addr.s_addr = INADDR_ANY;
oAddress.sin_port = htons(SERVER_PORT);
nMainSocketId = socket(AF_INET, SOCK_STREAM, 0);
if (nMainSocketId < 0) {
perror("server: socket() failed");
return 1;
}
if (bind(nMainSocketId, (struct sockaddr *) &oAddress, sizeof(oAddress))) {
perror("server: bind() failed");
terminateServer();
return 1;
}
printf("HTTP server listening on port %d\n", SERVER_PORT);
while (bExiting == 0) {
if (listen(nMainSocketId, 10) < 0) {
perror("server: listen() failed");
terminateServer();
return 1;
}
nReqSocketId = accept(nMainSocketId, (struct sockaddr *) &oAddress, &nAddrLen);
if (bExiting) { break; }
if (nReqSocketId < 0) {
perror("server: accept() failed");
terminateServer();
return 1;
}
nRecvResult = recv(nReqSocketId, sRequest, REQUEST_SIZE, 0);
if (nRecvResult < 0) {
perror("server: recv() failed");
terminateServer();
return 1;
}
if (nRecvResult == 0) {
printf("The client is disconnected. Waiting for another connection...\n");
close(nReqSocketId);
continue;
}
printf("The client is connected...\n\n%s\n", sRequest);
nRespFD = fdopen(nReqSocketId, "a+");
fprintf(nRespFD, "HTTP/1.1 200 OK\n");
fprintf(nRespFD, "Content-length: 50\n");
fprintf(nRespFD, "Content-Type: text/html\n\n");
fprintf(nRespFD, "<html><body><h1>Hello world!!!</h1></body></html>\n");
fclose(nRespFD);
close(nReqSocketId);
}
printf("Goodbye!\n");
return 0;
}
关于shutdown()与close(),你能更好地解释一下这个区别吗?
答案 5 :(得分:0)
好的,我的回复的第二部分解释了如何使您的Web服务器非持久性。 Persistent-Connections在HTTP 1.1,btw之前曾被称为Keep-Alive。因为我看到你每次发送后都关闭了你的响应套接字,所以也可能使web服务器“不持久”。
这意味着您不必因关闭()而发送“Content-Length:X”。您可以通过三种不同的方式关闭套接字:
"The constants SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, respectively..."
通过执行shutdown(SOCKET,1),我们实际上是向客户端(浏览器)发送FIN ack,让它知道套接字已完成写入。因此,无需设置“Content-Length:X”标头响应。
现在,这不是shutdown()VS close()的事情。这是一个shutdown()和close()的事情。您仍然必须关闭()套接字才能销毁它。关机不适合你。
总之,如果您不需要跟踪多个连接,请考虑从标头中取出“Content-Length:”。只需使用shutdown(SOCKET,1)作为“传输结束”机制,然后关闭以破坏套接字。
/ ---------------------------------------------- -------------------------------------------------- ---------------------------------- /
至于创建一个完整的FILE *只是为了发送字符串文字,我不确定是否值得。你有没有成功并得到一些结果?无论如何,我知道strcat()[当然包括strlen()]和strlen()的其他方法如果性能是你正在寻找的。我不知道是这样的。
PS-strcat()在开始时非常快,只有在连接大缓冲区时才开始增加其复杂性。查找SIMD以及如何根据您的体系结构优化某些类型的功能。
答案 6 :(得分:0)
在stdin和套接字上等待
这是一种特定于POSIX的行为,明确无法在Windows上运行。它依赖于以下事实:在* n * x平台上,套接字是文件描述符,它使用select
函数。
这是一个简化的函数,它将select
包装在单个套接字上等待。
/*Waits on a single socket and stdin.*/
int waitOnStdinAndSocket(
int sockFd, /*[in] Socket descriptor*/
int *pInputOnStdin, /*[out] Set to a nonzero value if there is input on stdin*/
int *pInputOnSocket, /*[out] Set to a nonzero value if there is input on the socket*/
sturct timeval *timeout /*[in/opt] Timeout*/
) /*Returns a negative value on failure.*/
{
int ret;
fd_set fds;
*pInputOnStdin = 0;
*pInputOnSocket = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
FD_SET(sockFd, &fds);
ret = select(sockFd+1, &fds, NULL, NULL, timeout);
if(ret >= 0)
{
*pInputOnStdin = FD_ISSET(STDIN_FILENO, &fds);
*pInputOnSocket = FD_ISSET(sockFd, &fds);
}
return ret;
}
答案 7 :(得分:0)
@Medinoc
非常感谢!
阅读完回复后,我在网上尝试了一下,找到了这个GNU页面:http://www.gnu.org/software/libc/manual/html_node/Server-Example.html。
所以,经过一些尝试,我已经能够将所有事情与我的“hello world”示例相结合。这是结果:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#define SERVER_PORT 80
#define REQUEST_MAX_SIZE 1024
char sRequest[REQUEST_MAX_SIZE];
int bListening = 1, nMainSocket;
void terminateServer () {
if (bListening == 0) { return; }
printf("\n\nTerminating server...\n");
close(nMainSocket);
bListening = 0;
}
void switchStdin () {
if (strcmp(sRequest, "q\n") == 0) {
terminateServer();
} else {
printf("Unknown request %s\n", sRequest);
}
}
void helloWorld (const int nRequestId) {
printf("The client is connected...\n\n%s\n", sRequest);
write(nRequestId, "HTTP/1.1 200 OK\n", 16);
write(nRequestId, "Content-length: 50\n", 19);
write(nRequestId, "Content-Type: text/html\n\n", 25);
write(nRequestId, "<html><body><h1>Hello world!!!</h1></body></html>\n", 50);
}
int main (void) {
int nOldReqSock, nNewReqSock, nReqLen, nUninitLen = REQUEST_MAX_SIZE;
fd_set oActiveFD, oReadFD;
socklen_t nAddrLen;
struct sockaddr_in oAddress;
oAddress.sin_family = AF_INET;
oAddress.sin_addr.s_addr = INADDR_ANY;
oAddress.sin_port = htons(SERVER_PORT);
/* Create the socket and set it up to accept connections. */
nMainSocket = socket(AF_INET, SOCK_STREAM, 0);
if (nMainSocket < 0) {
perror("socket");
return 1;
}
if (bind(nMainSocket, (struct sockaddr *) &oAddress, sizeof(oAddress))) {
perror("bind");
terminateServer();
return 1;
}
if (listen(nMainSocket, 10) < 0) {
perror("listen");
terminateServer();
return 1;
}
/* Initialize the set of active sockets plus STDIN. */
FD_ZERO(&oActiveFD);
FD_SET(STDIN_FILENO, &oActiveFD);
FD_SET(nMainSocket, &oActiveFD);
printf("\n W E L C O M E!\n\nType \"q\" to quit.\n");
while (bListening) {
/* Block until input arrives on one or more active sockets. */
oReadFD = oActiveFD;
if (select(FD_SETSIZE, &oReadFD, NULL, NULL, NULL) < 0) {
perror("select");
terminateServer();
return EXIT_FAILURE;
}
/* Service all the sockets with input pending. */
for (nOldReqSock = 0; bListening && nOldReqSock < FD_SETSIZE; ++nOldReqSock) {
if (FD_ISSET(nOldReqSock, &oReadFD)) {
if (nOldReqSock == nMainSocket) {
/* Connection request on original socket. */
nAddrLen = sizeof(oAddress); /* why??? */
nNewReqSock = accept(nMainSocket, (struct sockaddr *) &oAddress, &nAddrLen);
if (nNewReqSock < 0) {
perror("accept");
terminateServer();
return EXIT_FAILURE;
}
FD_SET(nNewReqSock, &oActiveFD);
} else {
/* Data arriving on an already-connected socket. */
nReqLen = read(nOldReqSock, sRequest, REQUEST_MAX_SIZE);
if (nReqLen < 0) {
/* Read error. */
perror("read");
terminateServer();
return EXIT_FAILURE;
} else if (nReqLen == 0) {
/* End-of-file. */
printf("End-of-file\n");
close(nOldReqSock); /* why??? */
FD_CLR(nOldReqSock, &oActiveFD); /* why??? */
continue;
} else {
/* Data read. */
if (nUninitLen > nReqLen) { memset(sRequest + nReqLen, 0, nUninitLen - nReqLen); }
nUninitLen = nReqLen;
}
if (nOldReqSock == STDIN_FILENO) {
/* Standard input received */
switchStdin(nReqLen);
} else {
/* TCP/IP request received */
helloWorld(nOldReqSock);
}
}
}
}
}
printf("Goodbye\n");
return 0;
}
我还在我不理解的行附近添加了一些/* why??? */
条评论。我可以请你简要解释一下它们是什么?