帮助我纠正此程序,我想创建一个可以无限期运行并可以服务许多客户端的服务器,为此,我正在使用UDP(用户数据报协议)应用程序的选择系统调用。
我的问题是,此代码正在不同的会话中为多个客户端运行,这意味着在为一个客户端运行后,它会停止,而当我再次启动服务器时,它又可以为另一个客户端提供服务。
我希望我的代码仅在一个会话中无限期工作,并根据需要服务尽可能多的客户端。
Server Code:-
'''
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define port1 8080
#define MAXN 1024
#define TRUE 1
int main(){
int sockfd,sockfd1;
struct sockaddr_in servaddr,cliaddr;
char buffer[MAXN];
char buff[MAXN];
int max_clients=2,valread,new_socket;
char *hello = "Hello Client";
char *message = "hiiii Server";
struct timeval timeout;
timeout.tv_sec = 20;
timeout.tv_usec = 0;
//create socket 2
sockfd = socket(AF_INET, SOCK_DGRAM,0);
if(sockfd<0){
perror("Error Creating Socket0");
exit(1);
}
//memset(&servaddr, 0, sizeof(servaddr));
//memset(&cliaddr, 0, sizeof(cliaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port1);
servaddr.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd, (const *)&servaddr, sizeof(servaddr))<0){
perror("Error in binding0 ");
exit(1);
}
//Use Select......
//I have created 2 socket having file deescriptor sockfd and sockfd1
int s;
int client_socket[2]={0,0};
fd_set readfds;
while(){
FD_ZERO(&readfds);
FD_SET(sockfd,&readfds);
//Let's say sockfd is max_fd
int max_fd = sockfd,sd,activity;
for(int i=0;i<2;i++){
sd = client_socket[i];
if(sd>0){
FD_SET(sd,&readfds);
}
if(sd>max_fd)
max_fd = sd;
}
activity = select( max_fd + 1 , &readfds , NULL , NULL , &timeout);
if ((activity < 0))
{
printf("select error");
}
int addrlen = sizeof(servaddr);
//If something happened on the master socket ,
//then its an incoming connection
if (FD_ISSET(sockfd, &readfds))
{
//inform user of socket number - used in send and receive commands
printf("New connection , socket fd is %d , ip is : %s , port : %d\n " , new_socket , inet_ntoa(servaddr.sin_addr) , ntohs
(servaddr.sin_port));
//send new connection greeting message
if( send(new_socket, message, strlen(message), 0) != strlen(message) )
{
perror("send");
}
puts("Welcome message sent successfully");
//add new socket to array of sockets
for (int i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
printf("Adding to list of sockets as %d\n" , i);
break;
}
}
}
//else its some IO operation on some other socket
for (int i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
//Check if it was for closing , and also read the
//incoming message
if ((valread = read( sd , buffer, 1024)) == 0)
{
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&servaddr , \
(socklen_t*)&addrlen);
printf("Host disconnected , ip %s , port %d \n" ,
inet_ntoa(servaddr.sin_addr) , ntohs(servaddr.sin_port));
//Close the socket and mark as 0 in list for reuse
//close( sd );
client_socket[i] = 0;
}
//Echo back the message that came in
else
{
//set the string terminating NULL byte on the end
//of the data read
buffer[valread] = '\0';
send(sd , buffer , strlen(buffer) , 0 );
}
}
}
}
return 0;
}
'''
This is one of the Client Code and this code is running :-
'''
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define PORT 8080
#define MAXN 1024
// Driver code
int main() {
int sockfd;
char buffer[MAXN];
char *hello = "Hello from Multipleclient";
struct sockaddr_in servaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("Error in socket creation");
exit(1);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
int n, len;
sendto(sockfd, (const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
printf("Hello message sent.\n");
n = recvfrom(sockfd, (char *)buffer, MAXN,
MSG_WAITALL, (struct sockaddr *) &servaddr,
&len);
buffer[n] = '\0';
printf("Server : %s\n", buffer);
close(sockfd);
return 0;
}
答案 0 :(得分:0)
从一些小事情开始:
在客户端代码中,不要在将len
传递给recvfrom
之前对其进行初始化。另外,它的类型错误(int
而不是socklen_t
)。可以通过将len
定义为socklen_t len = sizeof(servaddr);
来纠正此问题。但是,由于您没有在任何地方使用长度或地址,因此您只需为两者传递NULL
。另外,UDP不支持MSG_WAITALL
,因此请传递0
作为标志。 recvfrom(sockfd, (char *)buffer, MAXN, 0, NULL, NULL)
。
在客户端代码中,您将服务器IP地址填写为IPADDR_ANY
,即0.0.0.0
,仅在调用bind
时有效。您正在呼叫sendto
。指定目标地址时,您可能需要IPADDR_LOOPBACK
作为本地计算机。
在服务器代码中,主while()
循环缺少括号内的条件,因此该代码甚至无法编译。您可能希望while(1)
进行无限循环。
在服务器代码中,对bind
的调用将您转换为错误的类型。 (const *)
的意思是(const int *)
,但您想要(const struct sockaddr *)
。
最大的问题:
基于服务器代码中的注释和代码,您似乎认为UDP通信具有持久性连接,并尝试为它们保存client_socket
值。但是,UDP没有持久连接。在这种情况下,没有客户端套接字之类的东西。 select
不会告诉您新的“连接”已准备就绪。相反,它告诉您已经在您唯一的UDP套接字(sockfd
)上接收到数据。这与TCP完全不同,在TCP中,accept
一直持续到一侧终止连接为止。
从服务器代码中的注释来看,您似乎打算让客户端在服务器中具有持久状态,至少要在它们发送更多数据报时被识别(否则它们总是会收到欢迎消息)。
实现此目的的一种可能方法如下:
// create and bind a UDP socket as you already do;
// initialize and empty list of clients;
while(1) {
struct sockaddr_in cliaddr;
socklen_t cliaddr_len = sizeof(cliaddr);
recvfrom(sockfd, buffer, bufferlen, /*flags*/ 0, (struct sockaddr *)&cliaddr, &cliaddr_len);
// check whether cliaddr is in your list;
if (!is_in_list) {
// this is a new client
// add to list of clients;
// send welcome message;
}
// do something else, maybe echo back the buffer contents;
}
您会发现该代码实际上不再使用select
。如果您在等待时无事可做,则select
是不必要的,并且您仅监听一个套接字。如果您正在做问题中未显示的其他操作,则可以将select
添加回无限循环的顶部,并且仅在{{1}中设置了sockfd
时处理UDP套接字。 },来自readfds
。
还请注意,由于没有持久连接,并且客户端未将任何套接字传递给select
,因此客户端实际上无法从服务器接收答案。还要注意,UDP消息可能会在没有警告的情况下丢失,并且您应该确保您的代码可以处理该消息。看来使用TCP可以更轻松地实现这种客户端-服务器策略。
作为旁注,强烈建议在编译器中启用警告。这将为地址长度变量捕获错误的类型,并告诉您使用了bind
但从未设置。礼貌地在发布问题之前修正所有警告,除非问题当然与警告有关。