I'm writing a chat room program that communicates over network using TCP. If user provide ip address as a command line argument, the program would attempt to connect to that address. If not, server will wait for others to connect.
The server has no problem receiving whatever text message the client send. However, the client side only receives text messages from server only when it sends its own message. How do I fix that so that client side receives messages right away? This is my code
Server code:
#define MAX_CLIENTS 100
static unsigned int cli_count = 0;
static int uid = 10;
typedef struct {
struct sockaddr_in addr;
int connfd;
int uid;
char name[32];
} client_t;
client_t *clients[MAX_CLIENTS];
void queue_add(client_t *cl)
{
int i;
for(i=0;i<MAX_CLIENTS;i++)
{
if(!clients[i])
{
clients[i] = cl;
return;
}
}
}
void queue_delete(int uid)
{
int i;
for(i=0;i<MAX_CLIENTS;i++)
{
if(clients[i])
{
if(clients[i]->uid == uid)
{
clients[i] = NULL;
return;
}
}
}
}
void send_message_all(char *s)
{
int i;
for(i=0;i<MAX_CLIENTS;i++)
{
if(clients[i])
{
write(clients[i]->connfd, s, strlen(s));
}
}
}
void *hanle_client(void *arg)
{
char buff_in[256];
char buff_out[256];
int rlen;
cli_count++;
client_t *cli = (client_t *)arg;
sprintf(buff_out, "<<JOIN, HELLO %s\r\n", cli->name);
send_message_all(buff_out);
bzero(buff_in,sizeof(buff_in));
while((rlen = read( cli->connfd,buff_in,sizeof(buff_in)-1))>0)
{
sprintf(buff_out, "[%s] %s\r\n", cli->name, buff_in);
send_message_all(buff_out);
}
close(cli->connfd);
/* Delete client from queue and yeild thread */
queue_delete(cli->uid);
free(cli);
cli_count--;
pthread_detach(pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
int listenfd = 0, connfd = 0, portno;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
pthread_t tid;
if (argc < 2) {
printf("ERROR, no port provided\n");
exit(1);
}
//Create socket
listenfd= socket(AF_INET , SOCK_STREAM , 0);
if (listenfd == -1)
{
printf("Could not create socket");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(portno);
/* Bind */
if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
perror("Socket binding failed");
return 1;
}
/* Listen */
if(listen(listenfd, 10) < 0)
{
perror("Socket listening failed");
return 1;
}
printf("<[SERVER STARTED]>\n");
socklen_t clilen = sizeof(cli_addr);
/* Accept clients */
while( (connfd = accept(listenfd, (struct sockaddr *)&cli_addr, (socklen_t*)&clilen)))
{
/* Client settings */
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->addr = cli_addr;
cli->connfd = connfd;
cli->uid = uid++;
sprintf(cli->name, "%d", cli->uid);
/* Add client to the queue and fork thread */
queue_add(cli);
pthread_create(&tid, NULL, &hanle_client, (void*)cli);
}
}
Client code:
int main(int argc , char *argv[])
{
int sockfd, portno ;
struct sockaddr_in serv_addr;
struct hostent *server;
char message[2000],server_reply[2000];
if (argc <3)
{
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(1);
}
portno = atoi(argv[2]);
//Create socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("ERROR opening socket");
exit(1);
}
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
//Connect to remote server
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
perror("ERROR connecting");
exit(1);
}
puts("Connected\n");
//keep communicating with server
while(1)
{
//Receive a reply from the server
bzero(server_reply,2000);
if( recv(sockfd , server_reply , 2000,0) < 0)
{
puts("recv failed");
break;
}
printf("%s", server_reply);
server_reply[0]='\0';
//Send Message to server
printf("Enter Message:");
bzero(message,2000);
fgets(message, sizeof(message),stdin);
if(send(sockfd , message , strlen(message),0) < 0)
{
puts("Send failed");
return 0;
}
}
close(sockfd);
return 0;
}
I am not sure if I understood your problem correctly. But at a high level, I noticed that your hanleClient method calls close(cli->connfd) on the clients socket after calling sendall. After calling close, you are deleting the client details from the queue. This way, the client being deleted will never receive any future messages. Are you sure this is what you want?
Try removing these lines and check if that is what you want -
close(cli->connfd);
/* Delete client from queue and yeild thread */
queue_delete(cli->uid);
free(cli);
cli_count--;
This way, whenever the server receives a message, it will try to send it to all clients that are connected to the server.
Note: Your code is not thread safe and will result in unexpected behaviour since you are accessing global data from within threads without using mutexes.
Related
I have this ftserver.c program which is implementing a file transfer server which listens for a client and then responds to the clients request over a data connection. Right now it works but I have the hostname and port number for the data connection hardcoded. The portnumber has been provided by the client and the server should be able to get the hostname from the client's control connection.
References:http://beej.us/guide/bgnet/output/html/multipage/getaddrinfoman.html
How can I assign the hostname and portnumber dynamically? Thank you.
void error(const char *msg)
{
perror(msg);
exit(1);
}
void startup(int portNumber);
void setupData(char* portNum);
int sockfd, newsockfd, datasock, portno;
char buffer[256]; socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr, port_addr;
struct addrinfo hints, *servinfo, *p;
char ipstr[1000];
struct in_addr ipAddr;
struct sockaddr_in *s;
int main(int argc, char *argv[]){
int n; char* dataport; char * token; char filename[100];
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
portno = atoi(argv[1]);
startup(portno);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
token = strtok(buffer, " ");
//if client requested a list, setup data connection and send it
if (strcmp(token, "-l") == 0){
token = strtok(NULL, " ");
printf("the token is %s\n", token);
//dataport = atoi(token);
dataport = token;
setupData(dataport);
//sendList(dataport);
}
//if client requested a file, setup data connection and send it
else if (strcmp(token, "-g") == 0){
token = strtok(NULL, " ");
//filename = *token;
token = strtok(NULL, " ");
//dataport = atoi(token);
printf("the data port is %d\n", dataport);
//setupData(dataport);
//sendFile(filename, dataport);
}
else {
n = write(newsockfd,"not a valid command",19);
if (n < 0) error("ERROR writing to socket");
}
//close sockets for connection P
close(datasock);
close(newsockfd);
close(sockfd);
return 0;
}
void startup(int portNumber)
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portNumber);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,256);
}
void setupData(char* portNum){
int rv;
const char* name = "localhost";
char s[1000];
memset(&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(name, "30024", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(1);
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((datasock = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("socket");
continue;
}
if (connect(datasock, p->ai_addr, p->ai_addrlen) == -1) {
perror("connect");
close(sockfd);
continue;
}
break; // if we get here, we must have connected successfully
}
if (p == NULL) {
// looped off the end of the list with no connection
fprintf(stderr, "failed to connect\n");
exit(2);
}
printf("data connection setup successful\n");
}
I have resolved this. By using a simplified version of setting up the data conection, not using getaddrinfo() and using a hostname_to_ip conversion function. Reference: http://www.linuxhowtos.org/data/6/client.c
int setupData(char* hostname, char* portNum){
int sock_fd; char ip[100];
struct sockaddr_in srv_addr;
memset(&srv_addr, 0, sizeof(srv_addr)); /* zero-fill srv_addr structure*/
/* create a client socket */
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
srv_addr.sin_family = AF_INET; /* internet address family */
/* convert command line argument to numeric IP */
hostname_to_ip(hostname, ip);
printf("%s resolved to %s" , hostname , ip);
if ( inet_pton(AF_INET, ip, &(srv_addr.sin_addr)) < 1 )
{
printf("Invalid IP address\n");
exit(EXIT_FAILURE);
}
srv_addr.sin_port = htons(atoi(portNum));
if( connect(sock_fd, (struct sockaddr*) &srv_addr, sizeof(srv_addr)) < 0 )
{
perror("connect error");
exit(EXIT_FAILURE);
}
return sockfd;
}
I have written a sample socket program in C on Linux.
The server is single process server.
The program is simple where the server is running and the client connects to the server waiting on accept() call.
When the server accepts the client request it sends some string to the client using write call.
Server Code:
#define MAXHOSTNAME 256
#define MAX_CONN 10
void single_process_server(unsigned int portNumber)
{
int listenSockFd, acceptSockFd, portNo;
socklen_t clilen;
char buffer[256] = "Connected";
struct sockaddr_in srvInfo;
int n;
char sysHost[MAXHOSTNAME+1]; // Hostname of this computer we are running on
if((listenSockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
close(listenSockFd);
error("ERROR opening socket");
}
bzero((char *) &srvInfo, sizeof(srvInfo));
portNo = portNumber;
srvInfo.sin_family = AF_INET;
srvInfo.sin_addr.s_addr = htonl(INADDR_ANY);
srvInfo.sin_port = htons(portNo);
if (bind(listenSockFd, (struct sockaddr *) &srvInfo, sizeof(srvInfo)) < 0)
{
close(listenSockFd);
error("ERROR on binding");
}
listen(listenSockFd,5);
while(1)
{
clilen = sizeof(srvInfo);
if((acceptSockFd = accept(listenSockFd, (struct sockaddr *) &srvInfo, &clilen)) < 0)
{
error("ERROR on accept");
}
if((n = write(acceptSockFd,buffer,255)) < 0)
{
error("ERROR writing to socket");
}
close(acceptSockFd);
}
close(listenSockFd);
}
int main(int argc, char* argv[])
{
unsigned int portNo= 0;
portNo = 444;
single_process_server(portNo);
return(0);
}
The client receives the string using read call.
Client Code:
void simple_internet_client(char hostip[], unsigned int portNo)
{
int sockFd, portno, n;
struct sockaddr_in srvInfo;
struct hostent *server;
char buffer[256];
portno = portNo;
if((sockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
error("ERROR opening socket");
}
if((server = gethostbyname(hostip)) == NULL)
{
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &srvInfo, sizeof(srvInfo));
srvInfo.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&srvInfo.sin_addr.s_addr, server->h_length);
srvInfo.sin_port = htons(portno);
if(connect(sockFd,(struct sockaddr *) &srvInfo,sizeof(srvInfo)) < 0)
error("ERROR connecting");
{
bzero(buffer,256);
if((n = read(sockFd,buffer,255)) < 0)
error("ERROR reading from socket");
printf("Socket read = %s\n", buffer);
}
close(sockFd);
}
main(int argc, char* argv[])
{
char hostip[50] = {0};
unsigned int portNo= 444;
printf("B. Provide the host name or IP:\n");
scanf("%s",&hostip);
simple_internet_client(hostip, portNo);
return(0);
}
When I run the client(./clientdemo) at some intervals then the client receives whatever string the server sends.
But when I run the client multiple times through some script then the client remains stuck up at read() call and does not receive what the server has sent.
Simply speaking when the client connects to the server at faster rate then the server writes to the socket at same rate.But the client is not able to read the data written on the socket and remains stuck up on the read() call.
What may be the cause of this and why client is not able to read data from socket at same rate as server writes to it?
My mini project is on implementing a c socket program where multiple clients send files to two or three servers. i have implemented these. but for handling the client request i need to create a child process is it? how can i do that . like have to handle the request separately.please anybody guide me to do it.
server:
int main()
{
int sockfd, new_sockfd,x1,x2,log,n;
int server_len, client_len,len;
int cont,fh,cont2;
int result1;
struct sockaddr_in serveraddress;
struct sockaddr_in address;
struct sockaddr_in client_address;
FILE *ia_address;
char *fname = "/home/shishira/Desktop/packet_capture/info_agent_report.txt";
int buffsize=1024;
char buffer1[1024];
char buffer[1024];
char clntName[INET_ADDRSTRLEN];
if((sockfd = socket(AF_INET,SOCK_STREAM,0))>0)
printf("\n Socket was created\n");
/* Name the socket. */
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = ntohs(9734);
server_len = sizeof(address);
bind(sockfd, (struct sockaddr *)&address, server_len);
/* Create a connection queue and wait INFO_AGENT_REPORTS */
listen(sockfd, 5);
while(1)
{
char ch;
printf("\n\n Task agent waiting...\n");
/* Accept a connection to collect report from INFO_AGENT */
client_len = sizeof(client_address);
new_sockfd = accept(sockfd,(struct sockaddr *)&client_address, &client_len);
if (new_sockfd==-1) { perror("Connection Not Accepted!!"); return(1);}
else
{
printf("\n Information agent is connected\n");
if(inet_ntop(AF_INET,&client_address.sin_addr.s_addr,clntName,sizeof(clntName))!=NULL)
{
ia_address = fopen("info_agent_report.txt","a+");
fprintf(ia_address,"\nFrom InformationAgent:%s\n",clntName);
fclose(ia_address);
}
printf("\n Task agent received the contents and saved it in 'info_agent_report' file");
log=open("info_agent_report.txt",O_CREAT|O_RDWR|O_APPEND,0777);
if(log==-1)
{
perror("cannot open info_agent_report file\n");
return(1);
}
do
{
x1=read(new_sockfd, buffer1, 1024);
x2=write(log,buffer1,x1);
}
while (x1>0);
close(log);
close(new_sockfd);
}
/*this is to connect to other server */
/* connect socket to the interface server's socket. */
int interface_sockfd = socket(AF_INET,SOCK_STREAM,0);
serveraddress.sin_family = AF_INET;
serveraddress.sin_addr.s_addr = inet_addr("127.0.0.1");
serveraddress.sin_port = 9735;
len = sizeof(serveraddress);
//len=sizeof(address);
if((result1 = connect(interface_sockfd, (struct sockaddr *)&serveraddress, len))==0)
printf("\n Connecting to the Interface server\n");
if(result1 == -1)
{
perror(" Not able to connect to Interface Server!!!!\n");
}
fh = open(fname , O_RDONLY);
if(fh==-1)
{
perror(" INFO_AGENT_REPORT File is Not Opened!!\n");
return(1);
}
do
{
cont=read(fh, buffer, buffsize);
cont2=write(interface_sockfd,buffer,cont);
}
while (cont==1024);
close(fh);
printf("\n Task agent has sent 'info_agent_report' file to the Interface Server\n\n");
close(interface_sockfd);
}
}
getnameinfo() is more new-style.
You use it like
char clntName[INET6_ADDRSTRLEN];
char portName[6]; // I wonder if there is no appropriate constant...
if(getnameinfo(&client_address,sizeof client_address,clntName,sizeof(clntName),NULL,0,NI_NUMERICHOST|NI_NUMERICSERV|NI_NUMERICSCOPE)==0){
printf("Client = %s/%s\n",clntName,portName);
} else {
printf("Unable to get address\n");
}
Once you have done so, you won't have any difficulties mixing IPv4 and IPv6 calls.
Here is how : ( put this after accept ):
char clntName[INET_ADDRSTRLEN];
FILE *output;
if(inet_ntop(AF_INET,&client_address.sin_addr.s_addr,clntName,sizeof(clntName))!=NULL){
output = fopen("output.txt","a+");
fprintf(output,"%s%c%d",clntName,'/',ntohs(client_address.sin_port));
fclose(output);
} else {
printf("Unable to get address\n"); // i just fixed this to printf .. i had it as print before
}
I'm working on server-client application in IDE Momentics Tool.
This is the server part:
int sockfd, portno, a;
char *IP[16];
int server_to_client(struct AllPack allmess)
{
int portno;
struct sockaddr_in serv_addr;
struct hostent *server;
if (a < 2)
{
fprintf(stderr,"ERROR, no IP and port provided\n");
exit(1);
}
portno = atoi(IP[2]);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) error ("ERROR opening socket");
{
server = gethostbyname(IP[1]);
}
if (server == NULL)
{
fprintf(stderr, "ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *) server -> h_addr, (char *)&serv_addr.sin_addr.s_addr, server -> h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) error ("ERROR connecting");
int n = write(sockfd, &allmess, sizeof(allmess));
if (n < 0) error ("ERROR writing socket");
close(sockfd);
return (EXIT_SUCCESS);
}
int main(int argc, char *argv[])
{
a = argc;
for(int i = 0; i < a; i++)
{
IP[i] = argv[i];
}
server_to_client(allmess);
return (EXIT_SUCCESS);
}
I'm trying to make portno = atoi(IP[2]); an array of ports and server = gethostbyname(IP[1]); an array of IP-addresses.
I'm starting my server part like this:
/home/Server 192.168.0.21 70003 192.168.0.22 70004
so I have to send data to several IP through several ports at the same time?
I have a multi-client chat server and for some reason only the first client is being added. I used a tutorial to help get me started. I have included my code below. When I try and add another client it doesnt appear to be added. If I add one client I get a response from the server like I want but only the first message I enter then after that it stops sending correctly.
Server Code:
int main(void)
{
struct sockaddr_in my_addr, cli_addr[10],cli_temp;
int sockfd;
socklen_t slen[10],slen_temp;
slen_temp = sizeof(cli_temp);
char buf[BUFLEN];
int clients = 0;
int client_port[10];
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
{
printf("test\n");
err("socket");
}else{
printf("Server : Socket() successful\n");
}
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr* ) &my_addr, sizeof(my_addr))==-1)
{
err("bind");
}else{
printf("Server : bind() successful\n");
}
int num_clients = 0;
while(1)
{
//receive
printf("Receiving...\n");
if (recvfrom(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&cli_temp, &slen_temp)==-1)
err("recvfrom()");
if (clients <= 10) {
cli_addr[clients] = cli_temp;
client_port[clients] = ntohs(cli_addr[clients].sin_port);
clients++;
printf("Client added\n");
//printf("%d",clients);
int i;
for(i=0;sizeof(clients);i++) {
sendto(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&cli_addr[i], sizeof(cli_addr[i]));
}
}
}
close(sockfd);
return 0;
}
I have included the client code as well in case it helps.
void err(char *s)
{
perror(s);
exit(1);
}
sig_atomic_t child_exit_status;
void clean_up_child_process (int signal_number)
{
/* Clean up the child process. */
int status;
wait (&status);
/* Store its exit status in a global variable. */
child_exit_status = status;
}
int main(int argc, char** argv)
{
struct sockaddr_in serv_addr;
int sockfd, slen=sizeof(serv_addr);
char buf[BUFLEN];
struct sigaction sigchld_action;
memset (&sigchld_action, 0, sizeof (sigchld_action));
sigchld_action.sa_handler = &clean_up_child_process;
sigaction (SIGCHLD, &sigchld_action, NULL);
int pid,ppid;
if(argc != 2)
{
printf("Usage : %s <Server-IP>\n",argv[0]);
exit(0);
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
err("socket");
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_aton(argv[1], &serv_addr.sin_addr)==0)
{
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
pid = fork();
if (pid<0) {
err("Fork Error");
}else if (pid==0) {
//child process will receive from server
while (1) {
bzero(buf,BUFLEN);
//printf("Attempting to READ to socket %d: ",sockfd);
fflush(stdout);
//recvfrom here
if (recvfrom(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&serv_addr, &slen)==-1)
err("recvfrom()");
printf("The message from the server is: %s \n",buf);
if (strcmp(buf,"bye\n") == 0) {
ppid = getppid();
kill(ppid, SIGUSR2);
break;
}
}
}else {
//parent will send to server
while(1){
printf("Please enter the message to send: ");
bzero(buf,BUFLEN);
fgets(buf,BUFLEN,stdin);
printf("Attempting to write to socket %d: ",sockfd);
fflush(stdout);
//send to here
if (sendto(sockfd, buf, BUFLEN, 0, (struct sockaddr*)&serv_addr, slen)==-1)
{
err("sendto()");
}
}
}
close(sockfd);
return 0;
}
Several problems jump out at me. First, every time you receive a message it will consider that to be a new client. Instead of just incrementing the clients variable for a message, you'll need to scan through the array to see if the source address is already present. Second, sizeof(clients) will return a static value (probably 4) depending on how many bytes an int occupies on your machine. That loop should be for( int i = 0; i < clients; i++ ).
You also have a variable named num_clients which is not used. Is that supposed to be there for something and maybe is causing some confusion?
Finally, instead of using the magic value 10 all over the place, use #define MAX_CONNECTIONS 10 and then replace all those numbers with MAX_CONNECTIONS. It's a lot easier to read and change later.