gpt4 book ai didi

c - 带有套接字的C服务器/客户端

转载 作者:太空宇宙 更新时间:2023-11-04 10:33:59 25 4
gpt4 key购买 nike

[仅剩下两个小问题]
我试图用C编写一个简单的服务器/客户端,通过套接字发送消息。
它必须在Linux和Windows下运行。我发现了很多Linux的例子,但是很多例子都不支持Windows。
如果你能帮助我,那就太好了。
对于服务器,我有些不明白的地方。
我在服务器端做什么?
需要在Windows上初始化WSA,在Linux上不需要。
在服务器端为服务器创建套接字。
为服务器创建struct sockaddr\u in。
在任意IP上绑定套接字。
听一下插座。
接受连接,处理与newSocket的连接。
关闭新插座,重复6)。
只有当我不关闭newSocket时它才能正常工作,但是为什么呢?(EBADF错误)
Edit2之后的代码更新:

// IMPORTANT: On linker errors try -lws2_32 as Linkerparameter
#ifdef __WIN32__
# include <winsock2.h> // used for sockets with windows, needs startup / shutdown
# include <ws2tcpip.h> // for MinGW / socklen_t
# define INIT_SOCKET_SYSTEM WSADATA wsaData;if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {printf ("Error initialising WSA.\n");exit(6);}
# define CLEAR_SOCKET_SYSTEM WSACleanup();
# include <windows.h> // for Sleep
# define SLEEP Sleep(10); // sleeping 10ms
# define CLOSE_SOCKET_FUNCTION closesocket
#else
# include <sys/socket.h>
# define INIT_SOCKET_SYSTEM printf("Linux dont need a special init for sockets, so all fine.\n");
# define CLEAR_SOCKET_SYSTEM printf("Linux dont need a special clear for sockets, so all fine.\n");
# include <time.h>
# define SLEEP sleep(1); // sleeping a second :-/
# define CLOSE_SOCKET_FUNCTION close
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <strings.h> // used for bzero
//used in the tutorial but not necessary!?
//#include <sys/types.h>
//#include <unistd.h>
//#include <stdlib.h>


/* could be still useful
// polling 10ms...
//#include <time.h>
//#define SLEEP time_t tStart, tEnd;time(&tStart);do {time(&tEnd);} while (difftime(tEnd, tStart) < 0.01);
*/
/* Random Sources
* http://pubs.opengroup.org/onlinepubs/9699919799/
* http://linux.die.net/man/2
* http://stackoverflow.com/questions/31765278/simple-webserver-wont-work
* http://blog.stephencleary.com/2009/05/using-socket-as-server-listening-socket.html
*
* http://cs.baylor.edu/~donahoo/practical/CSockets/WindowsSockets.pdf
*/

// functions
void createListenSocket(int * retListenSocket, const int port, bool * isRunning);
void listenFor(int * listenSocket, bool * isRunning);
void acceptFor(int * listenSocket, socklen_t * addrlen, bool * isRunning);
void handleConnection(int * inSocket, struct sockaddr_in * addClient);

// http://www.gnu.org/software/libc/manual/html_node/Cleanups-on-Exit.html
int * cleanSocket;
void cleanUp() {
CLOSE_SOCKET_FUNCTION(* cleanSocket);
CLEAR_SOCKET_SYSTEM
}


//[todo] WSAGetLastError handling for windows
int main(int argc, char ** argv) {
atexit(cleanUp);
bool isRunning = true;
socklen_t addressLen = sizeof(struct sockaddr_in);

// create listening socket
const int port = 15000;
int listenSocket;
cleanSocket = &listenSocket;

createListenSocket(&listenSocket, port, &isRunning);
listenFor(&listenSocket, &isRunning);
while (isRunning) {
acceptFor(&listenSocket, &addressLen, &isRunning);
SLEEP
}

return 0;
}

void createListenSocket(int * retListenSocket, const int port, bool * isRunning) {
INIT_SOCKET_SYSTEM
struct sockaddr_in addServer;
(* retListenSocket) = socket(AF_INET, SOCK_STREAM, 0);
int tErr = errno;
if ((* retListenSocket) > 0) {
printf("The socket was created (%i)\n", * retListenSocket);
} else {
printf("Couldnt create socket\n- ");
switch (tErr) {
case EACCES:
printf("Permission to create a socket of the specified type and/or protocol is denied.\n");
break;
case EAFNOSUPPORT:
printf("The implementation does not support the specified addServer family.\n");
break;
case EINVAL:
printf("Unknown protocol, or protocol family not available. OR Invalid flags in type.\n");
break;
case EMFILE:
printf("Process file table overflow.\n");
break;
case ENFILE:
printf("The system limit on the total number of open files has been reached.\n");
break;
case ENOBUFS:
printf("Insufficient memory is available. The socket cannot be created until sufficient resources are freed.\n");
break;
case ENOMEM:
printf("Insufficient memory is available. The socket cannot be created until sufficient resources are freed.\n");
break;
case EPROTONOSUPPORT:
printf("The protocol type or the specified protocol is not supported within this domain.\n");
break;
default:
printf("unspecified error %i ... \n", tErr);
break;
}
* isRunning = false;
return;
}

addServer.sin_family = AF_INET;
addServer.sin_addr.s_addr = INADDR_ANY;
addServer.sin_port = htons(port);

if (bind(* retListenSocket, (struct sockaddr * ) &addServer, sizeof(struct sockaddr_in)) == 0) {
printf("Socket bind successfull\n");
} else {
printf("Socket bind failed\n");
* isRunning = false;
return;
}
}

// http://linux.die.net/man/2/listen / http://pubs.opengroup.org/onlinepubs/9699919799/
void listenFor(int * listenSocket, bool * isRunning) {
int t = listen(* listenSocket, 10);
int tErr = errno;
if (t < 0) {
printf("Error while listening\n- ");
//perror("server: listen");
switch (tErr) {
case EADDRINUSE:
printf("Another socket is already listening on the same port.\n");
break;
case EBADF:
printf("The argument sockfd is not a valid descriptor.\n");
break;
case ENOTSOCK:
printf("The argument sockfd is not a socket\n");
break;
case EOPNOTSUPP:
printf("The socket is not of a type that supports the listen() operation\n");
break;
default:
printf("Undefined Error%i\n", tErr);
break;
}
* isRunning = false;
}
}

// http://linux.die.net/man/2/accept / http://pubs.opengroup.org/onlinepubs/9699919799/
void acceptFor(int * listenSocket, socklen_t * addrlen, bool * isRunning) {
struct sockaddr_in addClient;
memset(&addClient, 0, sizeof(addClient));
int NewSocket = accept(* listenSocket, (struct sockaddr *) &addClient, addrlen);
int tErr = errno;

//write(NewSocket, "Hoi\n", 4);
if (tErr != 0) {
printf("Error while accepting\n- ");
switch (tErr) {
case EAGAIN:
printf("The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.\n");
break;
case EWOULDBLOCK:
printf("The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.\n");
break;
case EBADF:
printf("The descriptor is invalid\n");
break;
case ECONNABORTED:
printf("A connection has been aborted.\n");
break;
case EFAULT:
printf("The addr argument is not in a writable part of the user addServer space.\n");
break;
case EINTR:
printf("The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7).\n");
break;
case EINVAL:
printf("Socket is not listening for connections, or addrlen is invalid (e.g., is negative). or (accept4()) invalid value in flags\n");
break;
case EMFILE:
printf("The per-process limit of open file descriptors has been reached.\n");
break;
case ENFILE:
printf("The system limit on the total number of open files has been reached.\n");
break;
case ENOBUFS:
printf("Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory.\n");
break;
case ENOMEM:
printf("Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory.\n");
break;
case ENOTSOCK:
printf("The descriptor references a file, not a socket.\n");
break;
case EOPNOTSUPP:
printf("The referenced socket is not of type SOCK_STREAM.\n");
break;
case EPROTO:
printf("Protocol error\n");
break;
default:
printf("Undefined Error %i\n", tErr);
break;
}
* isRunning = false;
} else if (NewSocket != -1) {
handleConnection(&NewSocket, &addClient);
}
}

void handleConnection(int * inSocket, struct sockaddr_in * addClient) {
if (* inSocket > 0){
int bufferSize = 1024;
char * buffer = malloc(bufferSize);
memset(buffer, '\0', bufferSize);

char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n\r\n"
"<html><head><title>test</title>"
"<html><body><H1>Hello world</H1></body></html>";
printf("The Client is connected from %s ...\n", inet_ntoa((* addClient).sin_addr));
//[todo] handle full buffer
int received = recv(* inSocket, buffer, bufferSize, 0);
printf("%s\nbuffer size: %i\n", buffer, bufferSize);
send(* inSocket, response, strlen(response), 0);
printf("=> response send\n");
CLOSE_SOCKET_FUNCTION(* inSocket);
}
}

我在客户端做什么?
需要在Windows上初始化WSA,在Linux上不需要。
在客户端为客户端创建一个套接字。
为服务器创建struct sockaddr\u in。
[尝试1]
为客户端创建struct sockaddr\u in。
将套接字绑定到客户端的结构。
使用客户端套接字连接到服务器结构。
发送消息。
[尝试2]
使用send to,因为我只想发送一条消息。
两者都不起作用,我想我的问题是struct sockaddr_-in,但我不知道为什么。
我做错什么了?
查看编辑3以获取解决方案。
#ifdef __WIN32__
# include <winsock2.h> // used for sockets with windows, needs startup / shutdown
# include <ws2tcpip.h> // for MinGW / socklen_t / InetPtonA
# define INIT_SOCKET_SYSTEM WSADATA wsaData;if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {printf ("Error initialising WSA.\n");exit(6);}
# define CLEAR_SOCKET_SYSTEM WSACleanup();
# include <windows.h> // for Sleep
# define SLEEP Sleep(10); // sleeping 10ms
#else
# include <sys/socket.h>
# define INIT_SOCKET_SYSTEM printf("Linux dont need a special init for sockets, so all fine.\n");
# define CLEAR_SOCKET_SYSTEM printf("Linux dont need a special clear for sockets, so all fine.\n");
# include <time.h>
# define SLEEP sleep(1); // sleeping a second :-/
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <stdbool.h>

// Step 1, create lokal Access point
void createSocket(int * mySocket);
// Step 2, create the target address
struct sockaddr_in getTargetAddress(char * ip, int port);

int * cleanSocket;
void cleanUp() {
close(* cleanSocket);
CLEAR_SOCKET_SYSTEM
}

int main() {
int mySocket;
// Step 1 create you Socket
createSocket(&mySocket);
// Step 2 get target
struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
//struct sockaddr_in myAddress = getTargetAddress("127.0.0.1", 15000);
// Step 3 bind & connect or sendto
//bind(mySocket, (const struct sockaddr *) &myAddress, sizeof(myAddress));
//connect(mySocket, (const struct sockaddr * )&serverAddress, sizeof(serverAddress));
char * question = "Whats up?\n";
printf("sending %s\n", question);
//send(mySocket, question, strlen(question), 0); // try to use protocol?
sendto(mySocket, question, strlen(question), 0, (const struct sockaddr *) &serverAddress, sizeof(serverAddress));
printf("sended!\n");

close(mySocket);
return 0;
}

void createSocket(int * mySocket) {
INIT_SOCKET_SYSTEM
if ((* mySocket = socket(AF_INET, SOCK_STREAM, 0)) > 0) {
printf("Socket creation successful\n");
} else {
printf("Socket creation failed\n");
}
}

struct sockaddr_in getTargetAddress(char * ip, int port) {
struct sockaddr_in ret;
ret.sin_family = AF_INET;
ret.sin_addr.s_addr = inet_addr(ip);
ret.sin_port = htons(15000);
return ret;
}

编辑1
评论包括:
我没有任何编译器错误,只是一个警告,因为 int received不在使用中。
我之所以这么评论是因为我做了很多尝试,想在我把它贴到这里之前把它清理干净,但我认为它可能足够重要,可以作为评论保留下来。
也许它包含在另一个包含中?我去查一下。
我现在在windows上测试和编写,但最后它也需要在linux上运行。我使用Autoit中的一个小工具在同一台连接到服务器并执行GET请求的计算机上的windows上测试上述服务器。服务器得到了GET,在控制台中打印了它,并发送了一个回复,Autoit客户端得到并打印了这个回复,所以它只工作了一次。
没有近距离手术,我每次都能做到。
编辑2-得到服务器的答案,客户端仍然没有运行
服务器现在运行正常,从以下地址获得答案:
http://cs.baylor.edu/~donahoo/practical/CSockets/WindowsSockets.pdf
从UNIX套接字迁移到Windows套接字相当简单。Windows程序需要不同的include文件集,需要初始化和释放WinSock资源,使用closesocket()而不是close(),并使用不同的错误报告工具。但是,应用程序的主要部分与UNIX相同。
编辑3-客户端工作,但有一个小问题
需要缩短链接,因为我不允许直接发布这么多链接。
我在try 1中的错误是将客户端结构绑定到与服务器相同的IP。
“127.0.0.1”(clientaddress)=>“Pseudo”及其工作原理。
mySocket = socket(AF_INET, SOCK_STREAM, 0)
struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
struct sockaddr_in myAddress = getTargetAddress("Pseudo", 15000);
bind(mySocket, (const struct sockaddr *) &myAddress, sizeof(myAddress));
connect(mySocket, (const struct sockaddr * )&serverAddress, sizeof(serverAddress));
send(mySocket, question, strlen(question), 0);

但我不需要自己绑定在这里,如果没有绑定,connect会执行,它会处理一个未使用的地址。
pubs.opengroup[.]org/onlinepubs/9699919799/functions/connect.html
如果套接字尚未绑定到本地地址,则connect()应将其绑定到一个地址,除非套接字的地址系列是AF_UNIX,否则该地址是未使用的本地地址。
mySocket = socket(AF_INET, SOCK_STREAM, 0)
struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
connect(mySocket, (const struct sockaddr * )&serverAddress, sizeof(serverAddress));
send(mySocket, question, strlen(question), 0);

这当然有效,但在我看来是不对的。
mySocket = socket(AF_INET, SOCK_STREAM, 0)
struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
connect(mySocket, (const struct sockaddr * )&serverAddress, sizeof(serverAddress));
sendto(mySocket, question, strlen(question), 0, (const struct sockaddr *) &serverAddress, sizeof(serverAddress));

在我看来,这里也应该有效,我不需要在这个结构中连接,因为它应该在sendto中构建。
pubs.opengroup[.]org/onlinepubs/9699919799/functions/connect.html
函数的作用是:尝试在连接模式套接字[…]上建立连接
pubs.opengroup[.]org/onlinepubs/969991999/functions/sendto.html
如果套接字是连接模式,则应忽略dest_addr。
由于上面的文字,我认为这也应该起作用,但它没有。也许有人能说为什么?(或者它应该在没有myAddress和bind的情况下工作)
mySocket = socket(AF_INET, SOCK_STREAM, 0)
struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
struct sockaddr_in myAddress = getTargetAddress("Pseudo", 15000);
bind(mySocket, (const struct sockaddr *) &myAddress, sizeof(myAddress));
sendto(mySocket, question, strlen(question), 0, (const struct sockaddr *) &serverAddress, sizeof(serverAddress));

顺便说一句,send和sendto的返回值不清楚。
成功完成对send()的调用不能保证消息的传递。返回值-1仅表示本地检测到的错误。
成功完成对sendto()的调用不能保证消息的传递。返回值-1仅表示本地检测到的错误。
我觉得回报值没用,还是没用?如果它是-1,它可以交付,如果1,它可能不是。
或许可以确定另一个方案?
小问题
为什么我还需要连接sendto?
我能用另一个协议从send/sendto得到一个明确的返回值吗?
如果我找到了答案,会在这里搜索并编辑它,还会观察是否有人能回答这个问题。
我的主要问题都解决了,真是多亏了大家。
谢谢你的阅读!

最佳答案

它不会起作用,因为您从未调用connect()。您应该检查connect()、send()等调用的返回值。
解决方案:应该在创建套接字后调用connect。

connect(mySocket, (SOCKADDR *)&serverAddress, sizeof(sockaddr_in));  

要将send或send To与TCP一起使用,套接字已连接,否则将出现错误。
send(mySocket, question, strlen(question), 0);

关于c - 带有套接字的C服务器/客户端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38416571/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com