Winsock 回显服务器端口
Winsock echo server port
我正在尝试将这个简单的 TCP 回显程序 (https://github.com/mafintosh/echo-servers.c/blob/master/tcp-echo-server.c) 移植到 Windows 下用于教学目的。
我的改编编译和运行,但它不起作用:
**** 编辑:监听电话不知何故被切断了。感谢雷米 ****
客户端已连接,但未收到任何回显。
移植代码如下(报错信息是意大利语,不过应该很清楚):
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#define BUFFER_SIZE 1024
void on_error(char *s) { fprintf(stderr,"%s\n",s); fflush(stderr); exit(1); }
int main(int argc, char *argv[]) {
WSADATA wsadata;
int server_fd, client_fd, err;
struct sockaddr_in server, client;
char buf[BUFFER_SIZE];
int port = 6666;
int risultato = WSAStartup(MAKEWORD(2,2),&wsadata);
if (risultato != NO_ERROR)
{fprintf(stderr,"Errore in WSAStartup");fflush(stderr); exit(1);}
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) on_error("Non ho potuto creare il socket\n");
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
const char opt_val = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof opt_val);
/** bind & listen **/
err = bind(server_fd, (struct sockaddr *) &server, sizeof(server));
if (err < 0) on_error("Non ho potuto fare il bind del socket\n");
err = listen(server_fd, 128);
if (err < 0) on_error("Non ho potuto mettermi in ascolto sul socket\n");
printf("SERVER LISTENING ON PORT %d\n", port);
while (1) {
int client_len = sizeof(client);
do {
client_fd = accept(server_fd, (struct sockaddr *) &client, &client_len);
} while ( client_fd = SOCKET_ERROR);
if (client_fd < 0) on_error("Non riesco a stabilire una nuova connessione\n");
while (1) {
int read = recv(client_fd, buf, BUFFER_SIZE, 0);
if (!read) break;
if (read < 0) on_error("Errore nella lettura dal client\n");
err = send(client_fd, buf, read, 0);
if (err < 0) on_error("Errore nella scrittura verso il client\n");
}
}
WSACleanup();
return 0;
}
您正在调用 bind()
来设置监听端口,但您没有调用 listen()
在进入 accept()
循环之前实际开始监听该端口。
一旦你修正了这个错误,你的 accept()
循环就会被打破,因为即使 accept()
成功了,它也会强制 client_fd
到 SOCKET_ERROR
。当需要使用 ==
比较运算符时,您的 while()
条件正在使用 =
赋值运算符。你应该检查 INVALID_SOCKET
而不是 SOCKET_ERROR
.
话虽如此,还有一些其他事情需要考虑:
WinSock 没有使用int
来表示套接字,而是使用SOCKET
来表示,这是一个UINT_PTR
。检查无效套接字句柄时,不要使用 < 0
,而应使用 == INVALID_SOCKET
。
大多数套接字功能 return 通过 WSAGetLastError()
的错误代码(WSAStartup()
是一个例外)。养成使用它的习惯,并在输出消息中报告错误代码,这样您就知道失败的原因。
SO_REUSEADDR
需要 BOOL
值,而不是 char
值。 BOOL
是 int
的类型定义,因此是 4 个字节。
并非所有套接字错误都是致命的,因此如果 recv()
/send()
操作因非致命错误而失败,您不应该终止整个服务器。
send()
不能保证发送您要求发送的所有内容,因此您应该考虑到这一点。
使用完后不要忘记关闭已接受的客户端套接字。
试试这个:
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#define BUFFER_SIZE 1024
void on_error(char *s, int *errCode = NULL)
{
int err = (errCode) ? *errCode : WSAGetLastError();
fprintf(stderr, "%s: %d\n", s, err);
fflush(stderr);
exit(1);
}
int main(int argc, char *argv[])
{
WSADATA wsadata;
SOCKET server_fd, client_fd;
struct sockaddr_in server, client;
int port = 6666, err;
char buf[BUFFER_SIZE];
err = WSAStartup(MAKEWORD(2,2), &wsadata);
if (err != 0)
on_error("Errore in WSAStartup", &err);
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == INVALID_SOCKET)
on_error("Non ho potuto creare il socket");
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = INADDR_ANY;
/** bind & listen **/
const BOOL opt_val = TRUE;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt_val, sizeof(opt_val));
err = bind(server_fd, (struct sockaddr *) &server, sizeof(server));
if (err == SOCKET_ERROR)
on_error("Non ho potuto fare il bind del socket");
err = listen(server_fd, 1);
if (err == SOCKET_ERROR)
on_error("Non ho potuto mettermi in ascolto sul socket");
printf("SERVER LISTENING ON PORT %d\n", port);
while (1)
{
int client_len = sizeof(client);
client_fd = accept(server_fd, (struct sockaddr *) &client, &client_len);
if (client_fd == INVALID_SOCKET)
on_error("Non riesco a stabilire una nuova connessione");
bool keepLooping = true;
do
{
int read = recv(client_fd, buf, BUFFER_SIZE, 0);
if (read == 0)
break;
if (read == SOCKET_ERROR)
{
err = WSAGetLastError();
if ((err != WSAENOTCONN) && (err != WSAECONNABORTED) && (err == WSAECONNRESET))
on_error("Errore nella lettura dal client", &err);
break;
}
char *pbuf = buf;
do
{
int sent = send(client_fd, pbuf, read, 0);
if (sent == SOCKET_ERROR)
{
err = WSAGetLastError();
if ((err != WSAENOTCONN) && (err != WSAECONNABORTED) && (err == WSAECONNRESET))
on_error("Errore nella scrittura verso il client", &err);
keepLooping = false;
break;
}
pbuf += sent;
read -= sent;
}
while (read > 0);
}
while (keepLooping);
closesocket(client_fd);
}
WSACleanup();
return 0;
}
对不起,但是
while (client_fd = SOCKET_ERROR)
你不是说吗?
(client_fd == SOCKET_ERROR)
我正在尝试将这个简单的 TCP 回显程序 (https://github.com/mafintosh/echo-servers.c/blob/master/tcp-echo-server.c) 移植到 Windows 下用于教学目的。 我的改编编译和运行,但它不起作用:
**** 编辑:监听电话不知何故被切断了。感谢雷米 ****
客户端已连接,但未收到任何回显。
移植代码如下(报错信息是意大利语,不过应该很清楚):
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#define BUFFER_SIZE 1024
void on_error(char *s) { fprintf(stderr,"%s\n",s); fflush(stderr); exit(1); }
int main(int argc, char *argv[]) {
WSADATA wsadata;
int server_fd, client_fd, err;
struct sockaddr_in server, client;
char buf[BUFFER_SIZE];
int port = 6666;
int risultato = WSAStartup(MAKEWORD(2,2),&wsadata);
if (risultato != NO_ERROR)
{fprintf(stderr,"Errore in WSAStartup");fflush(stderr); exit(1);}
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) on_error("Non ho potuto creare il socket\n");
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
const char opt_val = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof opt_val);
/** bind & listen **/
err = bind(server_fd, (struct sockaddr *) &server, sizeof(server));
if (err < 0) on_error("Non ho potuto fare il bind del socket\n");
err = listen(server_fd, 128);
if (err < 0) on_error("Non ho potuto mettermi in ascolto sul socket\n");
printf("SERVER LISTENING ON PORT %d\n", port);
while (1) {
int client_len = sizeof(client);
do {
client_fd = accept(server_fd, (struct sockaddr *) &client, &client_len);
} while ( client_fd = SOCKET_ERROR);
if (client_fd < 0) on_error("Non riesco a stabilire una nuova connessione\n");
while (1) {
int read = recv(client_fd, buf, BUFFER_SIZE, 0);
if (!read) break;
if (read < 0) on_error("Errore nella lettura dal client\n");
err = send(client_fd, buf, read, 0);
if (err < 0) on_error("Errore nella scrittura verso il client\n");
}
}
WSACleanup();
return 0;
}
您正在调用 bind()
来设置监听端口,但您没有调用 listen()
在进入 accept()
循环之前实际开始监听该端口。
一旦你修正了这个错误,你的 accept()
循环就会被打破,因为即使 accept()
成功了,它也会强制 client_fd
到 SOCKET_ERROR
。当需要使用 ==
比较运算符时,您的 while()
条件正在使用 =
赋值运算符。你应该检查 INVALID_SOCKET
而不是 SOCKET_ERROR
.
话虽如此,还有一些其他事情需要考虑:
WinSock 没有使用
int
来表示套接字,而是使用SOCKET
来表示,这是一个UINT_PTR
。检查无效套接字句柄时,不要使用< 0
,而应使用== INVALID_SOCKET
。大多数套接字功能 return 通过
WSAGetLastError()
的错误代码(WSAStartup()
是一个例外)。养成使用它的习惯,并在输出消息中报告错误代码,这样您就知道失败的原因。SO_REUSEADDR
需要BOOL
值,而不是char
值。BOOL
是int
的类型定义,因此是 4 个字节。并非所有套接字错误都是致命的,因此如果
recv()
/send()
操作因非致命错误而失败,您不应该终止整个服务器。send()
不能保证发送您要求发送的所有内容,因此您应该考虑到这一点。使用完后不要忘记关闭已接受的客户端套接字。
试试这个:
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#define BUFFER_SIZE 1024
void on_error(char *s, int *errCode = NULL)
{
int err = (errCode) ? *errCode : WSAGetLastError();
fprintf(stderr, "%s: %d\n", s, err);
fflush(stderr);
exit(1);
}
int main(int argc, char *argv[])
{
WSADATA wsadata;
SOCKET server_fd, client_fd;
struct sockaddr_in server, client;
int port = 6666, err;
char buf[BUFFER_SIZE];
err = WSAStartup(MAKEWORD(2,2), &wsadata);
if (err != 0)
on_error("Errore in WSAStartup", &err);
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == INVALID_SOCKET)
on_error("Non ho potuto creare il socket");
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = INADDR_ANY;
/** bind & listen **/
const BOOL opt_val = TRUE;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt_val, sizeof(opt_val));
err = bind(server_fd, (struct sockaddr *) &server, sizeof(server));
if (err == SOCKET_ERROR)
on_error("Non ho potuto fare il bind del socket");
err = listen(server_fd, 1);
if (err == SOCKET_ERROR)
on_error("Non ho potuto mettermi in ascolto sul socket");
printf("SERVER LISTENING ON PORT %d\n", port);
while (1)
{
int client_len = sizeof(client);
client_fd = accept(server_fd, (struct sockaddr *) &client, &client_len);
if (client_fd == INVALID_SOCKET)
on_error("Non riesco a stabilire una nuova connessione");
bool keepLooping = true;
do
{
int read = recv(client_fd, buf, BUFFER_SIZE, 0);
if (read == 0)
break;
if (read == SOCKET_ERROR)
{
err = WSAGetLastError();
if ((err != WSAENOTCONN) && (err != WSAECONNABORTED) && (err == WSAECONNRESET))
on_error("Errore nella lettura dal client", &err);
break;
}
char *pbuf = buf;
do
{
int sent = send(client_fd, pbuf, read, 0);
if (sent == SOCKET_ERROR)
{
err = WSAGetLastError();
if ((err != WSAENOTCONN) && (err != WSAECONNABORTED) && (err == WSAECONNRESET))
on_error("Errore nella scrittura verso il client", &err);
keepLooping = false;
break;
}
pbuf += sent;
read -= sent;
}
while (read > 0);
}
while (keepLooping);
closesocket(client_fd);
}
WSACleanup();
return 0;
}
对不起,但是
while (client_fd = SOCKET_ERROR)
你不是说吗?
(client_fd == SOCKET_ERROR)