I'm facing a problem with sockets programming in C.
I've coded a server where multiple clients can connect and send messages in a chatroom but i don't know how to connect two clients two by two.
example:
Client A connect to the Server but must wait in a queue waiting for Client B to connect.
Client C connect but must wait in a queue waiting for Client D to connect.
Every pair of clients must be in their specific chatroom, do I need to share the fd between the two sockets of the 2 clients or link between the two sockets.
I thought about using another way by using the function accept() twice before I fork() but I have to use child processes for that.
Here is my server code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT "8888"
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
fd_set master;
fd_set read_fds;
int fdmax;
int listener;
int newfd;
struct sockaddr_storage remoteaddr;
socklen_t addrlen;
char buf[256];
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1;
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master);
FD_ZERO(&read_fds);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "Server msg: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next)
{
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "Server msg: bind failed\n");
exit(2);
}
freeaddrinfo(ai);
puts("Bind success");
if (listen(listener, 10) == -1)
{
perror("listen");
exit(3);
}
puts("Server listening ...");
FD_SET(listener, &master);
fdmax = listener;
for(;;)
{
read_fds = master;
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(4);
}
for(i = 0; i <= fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{
if (i == listener)
{
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1)
{
perror("accept");
}
else
{
FD_SET(newfd, &master);
if (newfd > fdmax)
{
fdmax = newfd;
}
printf("Server msg: new connection from %s on "
"socket %d\n", inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN), newfd);
}
}
else
{
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0)
{
if (nbytes == 0)
{
printf("Server msg: socket %d lost\n", i);
}
else
{
perror("recv");
}
close(i);
FD_CLR(i, &master);
}
else
{
for(j = 0; j <= fdmax; j++)
{
if (FD_ISSET(j, &master))
{
if (j != listener && j != i)
{
if (send(j, buf, nbytes, 0) == -1)
{
perror("send");
}
}
}
}
}
}
}
}
}
return 0;
}
And here is my client code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <arpa/inet.h>
#define PORT "8888"
#define MAXDATASIZE 100
#define MAXNAMESIZE 25
void *receive_handler(void *);
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
char message[MAXDATASIZE];
char nickName[MAXNAMESIZE];
int sockfd;
char sBuf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"Usage: ./client address\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("Client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("Client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "Client: connection failed\n");
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
s, sizeof s);
printf("Client: connection to %s\n", s);
freeaddrinfo(servinfo);
puts("Give your username:");
memset(&nickName, sizeof(nickName), 0);
memset(&message, sizeof(message), 0);
fgets(nickName, MAXNAMESIZE, stdin);
pthread_t recv_thread;
if( pthread_create(&recv_thread, NULL, receive_handler, (void*)(intptr_t) sockfd) < 0)
{
perror("Failed on thread creation");
return 1;
}
puts("Connection established");
puts("Welcome!\n");
puts("[Type '/quit' to quit the chatroom]");
for(;;)
{
char temp[6];
memset(&temp, sizeof(temp), 0);
memset(&sBuf, sizeof(sBuf), 0);
fgets(sBuf, 100, stdin);
if(sBuf[0] == '/' &&
sBuf[1] == 'q' &&
sBuf[2] == 'u' &&
sBuf[3] == 'i' &&
sBuf[4] == 't')
return 1;
int count = 0;
while(count < strlen(nickName))
{
message[count] = nickName[count];
count++;
}
count--;
message[count] = ':';
count++;
for(int i = 0; i < strlen(sBuf); i++)
{
message[count] = sBuf[i];
count++;
}
message[count] = '\0';
if(send(sockfd, message, strlen(message), 0) < 0)
{
puts("Sent failed");
return 1;
}
memset(&sBuf, sizeof(sBuf), 0);
}
pthread_join(recv_thread , NULL);
close(sockfd);
return 0;
}
void *receive_handler(void *sock_fd)
{
int sFd = (intptr_t) sock_fd;
char buffer[MAXDATASIZE];
int nBytes;
for(;;)
{
if ((nBytes = recv(sFd, buffer, MAXDATASIZE-1, 0)) == -1)
{
perror("recv");
exit(1);
}
else
buffer[nBytes] = '\0';
printf("%s", buffer);
}
}
Yes, in my opinion you can use two accept and then call a fork() or, even better, a new thread. You can create a new process/thread for each pair of connections.
What do you mean sharing the fd? You mean sending a fd of one client to another? In this case NO
You can keep, in the thread function, a mapping between the two clients to know which clients are paired. When receiving a message from one client, thanks to the mapping, you know which client you are supposed to send the message to.
I think the rest depends on further details you should specify in your question.
Let me know
If each client can both send and receive you might want to use multithreading on client-side too: one to send and one to receive.
Related
I created a TCP client/server and was provided test script, however, beyond short messages, all tests are failing. Simply, the script send arbitrary messages that the client reads through redirection from a file to ther server. However with randomly created files by the script, it says that the messages on receving/sending side do not match. Any help will be appreciated, below is the client and server code.
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#define QUEUE_LENGTH 10
#define RECV_BUFFER_SIZE 2048
/* TODO: server()
* Open socket and wait for client to connect
* Print received message to stdout
* Return 0 on success, non-zero on failure
*/
int server(char *server_port) {
int sockfd, new_fd;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address
socklen_t sin_size;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int rv;
char buff[RECV_BUFFER_SIZE];
int numBytes;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my ip address
if ((rv = getaddrinfo(NULL, server_port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(servinfo); // all done with this structure
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, QUEUE_LENGTH) == -1) {
perror("listen");
exit(1);
}
// printf("server: waiting for connections...\n");
while (1) {
sin_size = sizeof their_addr;
if((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
perror("accept");
continue;
}
if (!fork()) { // child process
close(sockfd); // child does not need the listener
if ((numBytes = recv(new_fd, buff, RECV_BUFFER_SIZE -1, 0)) == -1) {
perror("recv");
exit(1);
}
buff[numBytes] = '\0';
printf("%s", buff);
close(new_fd);
exit(0);
}
close(new_fd);
}
return 0;
}
/*
* main():
* Parse command-line arguments and call server function
*/
int main(int argc, char **argv) {
char *server_port;
if (argc != 2) {
fprintf(stderr, "Usage: ./server-c [server port]\n");
exit(EXIT_FAILURE);
}
server_port = argv[1];
return server(server_port);
}
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#define SEND_BUFFER_SIZE 2048
/* TODO: client()
* Open socket and send message from stdin.
* Return 0 on success, non-zero on failure
*/
int client(char *server_ip, char *server_port)
{
int sockfd;
int status;
struct addrinfo hints, *servinfo, *p;
char send_buff[SEND_BUFFER_SIZE];
int numbytes;
char s[INET6_ADDRSTRLEN];
// getaddrinfo
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(server_ip, server_port, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getadrrinfo: %s\n", gai_strerror(status));
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1)
{
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
freeaddrinfo(servinfo);
// reading from stdin into send_buff, then send
if((numbytes = read(0, send_buff, SEND_BUFFER_SIZE)) != -1) {
if (send(sockfd, send_buff, numbytes, 0) == -1) {
perror("send");
exit(1);
}
}
close(sockfd);
return 0;
}
/*
* main()
* Parse command-line arguments and call client function
*/
int main(int argc, char **argv)
{
char *server_ip;
char *server_port;
if (argc != 3)
{
fprintf(stderr, "Usage: ./client-c [server IP] [server port] < [message]\n");
exit(EXIT_FAILURE);
}
server_ip = argv[1];
server_port = argv[2];
return client(server_ip, server_port);
}
2048 bytes exceed in size the typical MTU (note that TCP is laid over IP, which itself is packet oriented), so data is likely sent via multiple packets. As you have only one single call to recv chances are that you fetch the contents of first packet from the receive buffer before the the TCP/IP stack could place the contents of the follow up packets there.
Rather have multiple reads in a loop and exit the loop on receiving 0 bytes (remote socket closed):
while((numBytes = recv(new_fd, buff, RECV_BUFFER_SIZE -1, 0)) > 0)
{
buff[numBytes] = '\0';
printf("%s", buff);
}
if(numBytes < 0)
{
// error handling
}
So i have a client.c file on a computer in my lan and a server.c file on another computer. Essentially i start the server.c file and specify what port it needs to listen to as it waits. I think run my client.c command by specifying the address were server.c is running and what port to send my command on. the only thing my client sends is a filename (call it test.txt). My server is then to take that command using recv() from the C socket protocols, and i would like to be able to read that file and send that output back to my client so that it outputs to my clients terminal. I have figured out how to send the file as that is easy but i am having a hard time understanding how I can do this task with sockets. Do i just need to issue a write command using send() ? Do i need to read the file and send line by line to the socket ? if thats the case and say i have 10 clients do i need to use a semaphore or mutex to make sure that the output is sent fully before moving on to the next client request?
The code does more than just what I am asking for it also has a logging feature. and i have been using
http://beej.us/guide/bgnet/html/ as a guide. But its just sending back the contents of the file that is on the server.c machine to the client.c output that is kinda causing me to go in circles.
so in theory it should look something like this on the client side
./client 129.65.128.82:4443 bigfile
...entire contents of bigfile should appear here...
Here is my Server.c Code and Client.c (at the bottom for reference)
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#include <sys/stat.h>
#define PORT "4443" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
size_t getFilesize(const char* filename) {
struct stat st;
if(stat(filename, &st) != 0) {
return 0;
}
return st.st_size;
}
void fileOutput(char *filename){
FILE *fptr;
char c;
//int check = checkFilename(filename);
fptr = fopen(filename,"r");
printf("We opened the file\n");
if (fptr == NULL)
{
printf("Cannot open file \n");
exit(0);
}
printf("HereWe ARE\n");
// Read contents from file
c = fgetc(fptr);
while (c != EOF)
{
printf ("%c", c);
c = fgetc(fptr);
}
fclose(fptr);
}
void sigchld_handler(int s)
{
(void)s; // quiet unused variable warning
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while ( waitpid(-1, NULL, WNOHANG) > 0 );
errno = saved_errno;
}
void argumentCheck(int n){
if (n < 2) {
fprintf(stderr,"Usage: server port # example port 4443\n");
exit(1);
}
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if ( sa->sa_family == AF_INET ) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
void logfile(FILE *fp,char *s,char *port,char *buffer,int r){
time_t t = time((time_t*) NULL);
struct tm *tm = localtime(&t);
char tc[256];
strftime(tc, 256, "%Y-%m-%d %H:%M:%S", tm);
fprintf(fp,"[%s] %s:%s %s %d\n",tc,s,port,buffer,r);
}
int main(int argc, char **argv)
{
//Log file creation
FILE *fp;
fp = fopen("log","a");
///////
///File Size///
//////////////
argumentCheck(argc);
int sockfd, new_fd; // listen on sockfd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
char *inputPort = argv[1];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ( (rv = getaddrinfo(NULL, inputPort, &hints, &servinfo)) != 0 ) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ( (sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1 ) {
perror("server: socket");
continue;
}
if ( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1 ) {
perror("setsockopt");
exit(1);
}
if ( bind(sockfd, p->ai_addr, p->ai_addrlen) == -1 ) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(servinfo); // all done with this structure
if ( p == NULL ) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
// listen allows queue of up to BACKLOG number
if ( listen(sockfd, BACKLOG) == -1 ) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if ( sigaction(SIGCHLD, &sa, NULL) == -1 ) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while ( 1 ) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if ( new_fd == -1 ) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
//printf("server: got connection from %s\n", s);
if ( !fork() ) { // this is the child process
char buf[1024];
close(sockfd); // child doesn't need the listener
printf("this is the sizeof buf %ld\n",sizeof(buf));
recv(new_fd,buf, sizeof(buf),0);
int size = getFilesize(buf);
logfile(fp,s,inputPort,buf,size);
fileOutput(buf);
if ( send(new_fd, buf, size, 0) == -1)
perror("send");
printf("%s\n",buf);
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
fclose(fp);
return 0;
}
Client.c code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define PORT "4443" // the port client will be connecting to
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if ( sa->sa_family == AF_INET ) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char **argv)
{
if ( argc < 2 ) {
fprintf(stderr,"Usage: client ipaddr:port # example 129.65.128.82:4443 [filename]\n");
exit(1);
}
int sockfd, numbytes;
char buf[BUFSIZ];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET_ADDRSTRLEN];
// Used to extract ipv4 and socket seperatly
char *ip_address = strtok(argv[1],":");
char *ip_port = strtok(NULL,":");
memset(&hints, 0, sizeof(hints)); // make sure the struct is empty
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ( (rv = getaddrinfo(ip_address, ip_port, &hints, &servinfo)) != 0 ) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ( (sockfd = socket(p->ai_family,
p->ai_socktype,
p->ai_protocol)) == -1 ) {
perror("client: socket");
continue;
}
if ( connect(sockfd, p->ai_addr, p->ai_addrlen) == -1 ) {
perror("client: connect");
close(sockfd);
continue;
}
break;
}
if ( p == NULL ) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
//printf("we are stopping here");
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
s, sizeof s);
printf("client: connecting to %s\n", s);
freeaddrinfo(servinfo); // all done with this structure
//strcpy(buf, "Hello World.\n");
//printf("this is argv2 size %ld\n",strlen(argv[2]));
strcpy(buf,argv[2]);
//printf("this is buf size %ld\n",strlen(buf));
//write(1, "client send: ", strlen("client send: "));
//write(1, buf, strlen(buf));
send(sockfd, buf, sizeof(buf), 0);
if ( (numbytes = recv(sockfd, buf, sizeof(buf)-1, 0)) == -1 ) {
printf("this is numbytes %d\n",numbytes);
perror("recv");
exit(1);
}
printf("this is numbytes %d\n",numbytes);
buf[numbytes] = '\0';
//write(1, "client recv: ", strlen("client recv: "));
//write(1, buf, strlen(buf));
close(sockfd);
return 0;
}
enter code here
I'm currently learning network programming in university and programmed a server. As we need this one for further progress I need to reuse the code every time. There is a random function with an error I don't really get because on the linux pcs at university everything compiles well only my mac which I do all my homework shows this error.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
//#define PORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
int random(int min, int max){
srand ( time(NULL) );
return min + rand() / (RAND_MAX / (max - min + 1) + 1);
}
void sigchld_handler(int s)
{
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
//int linetoread;
if (argc != 3) {
fprintf(stderr,"usage: server port txtfile\n");
exit(1);
}
FILE* fp;
char buffer[255];
int i = 0;
char rnd[200][255];
int fixed;
fp = fopen(argv[2], "r");
while(fgets(buffer, 255, (FILE*) fp)) {
//printf("%s\n", buffer);
for (int j = 0; j < 255; j++){
rnd[i][j] = buffer[j];
}
i++;
}
fclose(fp);
//printf("number of lines: %d\n", i);
//linetoread = rand() % i;
//printf("%d\n", random(0,i));
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, argv[1], &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(servinfo); // all done with this structure
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
fixed = random(0,i-1);
//printf("line: %d\n", fixed);
if (send(new_fd, rnd[fixed], strlen(rnd[fixed]), 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}
Error on my mac:
server.c:16:5: error: conflicting types for 'random'
int random(int min, int max){
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include/stdlib.h:233:7: note:
previous declaration is here
long random(void) __swift_unavailable("Use arc4random instead.");
^
server.c:132:19: error: too many arguments to function call, expected 0, have 2
fixed = random(0,i-1);
~~~~~~ ^~~~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include/stdlib.h:233:1: note:
'random' declared here
long random(void) __swift_unavailable("Use arc4random instead.");
^
2 errors generated.
random() is a known function name. change it to something else and it will work.
my assignment was to build a chat server and client with the beej's guide examples so the client can upload a file to the server.
the instructions were to mmap the file and send the data using send().
iv'e tried first just to send simple txt file but it wont work.
for some reason when debug the program and execcutes it line by line it works.
maybe someone can point out what am i missing?
client code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PORT "6667" // the port client will be connecting to
#define MAXDATASIZE 256 // max number of bytes we can get at once
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
struct stat mystat;
char* pmap;
int fdin;
if (argc != 2) {
fprintf(stderr,"usage: client hostname\n");
exit(1);
}
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
s, sizeof s);
printf("client: connecting to %s\n", s);
freeaddrinfo(servinfo); // all done with this structure
FD_SET(sockfd,&master);
FD_SET(0,&master);
for(;;)
{
read_fds = master;
if (select(sockfd+1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(4);
}
if (FD_ISSET(0,&read_fds))
{
//reads messege form user
scanf ("%[^\n]%*c", buf);
//if it is upload
if (0 == strncmp(buf,"/upload ",8)) {
//sendFile(sockfd,buf);
if (send(sockfd,buf ,strlen(buf),0) == -1)
perror("send");
if ((fdin = open(buf + 8,O_RDONLY)) < 0)
perror("open");
fstat (fdin,&mystat);
pmap = mmap (0, mystat.st_size, PROT_READ, MAP_SHARED, fdin, 0);
if (send(sockfd,pmap,(int)mystat.st_size,0))
perror("send");
close(fdin);
}
else if (send(sockfd,buf,strlen(buf),0) == -1)
perror("send");
}
else if (FD_ISSET(sockfd, &read_fds))
{
if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1)
{
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("%s\n",buf);
}
}
close(sockfd);
return 0;
}
server code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#define PORT "6667" // port we're listening on
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
struct User *usersPtr = NULL; //ptr to users list
struct File *filesPtr = NULL; //ptr to files list
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
char msg[256]; //string to deal with message
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
char path[100],data[1000];
int fdout;
char* pmap;
int pid;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,(struct sockaddr *)&remoteaddr,&addrlen);
if (newfd == -1) {
perror("accept");
}
else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n",inet_ntop(remoteaddr.ss_family,get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),newfd);
}
}
else {
// handle data from a client
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
}
else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
}
else { // we got some data from a client
buf[nbytes] = '\0';
else if (0 == strncmp(buf,"/upload ",8)) {
sprintf(path,"%s",basename(buf + 8));
if((fdout = open (path, O_RDWR | O_CREAT | O_TRUNC, 0777)) < 0)
perror("open");
if ((nbytes = recv(i,data,sizeof data,0)) == -1)
perror("recive");
data[nbytes] = '\0';
lseek (fdout, nbytes - 1, SEEK_SET);
write (fdout, "", 1);
pmap = mmap (0, nbytes, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0);
memcpy (pmap , data, nbytes);
close(fdout);
}
else {
for(j = 0; j <= fdmax; j++) { // send to everyone!
if (FD_ISSET(j, &master)) {
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)
return 0;
}
Sockets are streams of data, sending 30 bytes then 100 will not result in the server reading 30 bytes then 100, it could read any amount up to 130 bytes. If your file is short, you will probably get the command and the data in one recv.
To get it working for short files containing text (not the complete solution).
The command must be \0 terminated for strlen to give the right size in the server so in the client change:
if (send(sockfd,buf ,strlen(buf),0) == -1)
to
if (send(sockfd,buf ,strlen(buf)+1,0) == -1)
The server will get the command and data together in buf, so in the server change:
char path[100],data[1000];
to
char path[100], *data;
and use data to point to the data in the buffer by changing:
if ((nbytes = recv(i,data,sizeof data,0)) == -1)
perror("recive");
data[nbytes] = '\0';
to
buf[nbytes] = '\0';
data = buf + strlen(buf) + 1;
nbytes = strlen(data);
The full solution is to loop receiving data and to look for the /upload command in stream. You could still look for the \0 as a delimiter, but you should then send the length of the file and use this to determine the number of bytes to read. This will cope with binary files too.
I am attempting to write a simple chat program where one user can connect to a host. The two should be able to talk to each other (they might interrupt each other but that is ok). I am having difficulties with select(). Here is the code:
client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define MAXDATASIZE 1024 // max number of bytes we can get at once
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*) sa)->sin_addr);
}
return &(((struct sockaddr_in6*) sa)->sin6_addr);
}
int main(int argc, char *argv[]) {
int sockfd, numbytes;
char buf[MAXDATASIZE];
char buffer[1024];
char *buff;
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
char *port;
int num;
struct timeval tv;
//select data
fd_set rfds;
fd_set wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
if (argc != 2) {
fprintf(stderr, "usage: client port\n");
exit(1);
}
port = argv[1];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo("localhost", port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *) p->ai_addr),
s, sizeof s);
printf("client: connecting to %s\n", s);
freeaddrinfo(servinfo); // all done with this structure
FD_SET(sockfd, &rfds);
FD_SET(sockfd, &wfds);
if (select(sockfd + 1, &rfds, &wfds, NULL, &tv) < 0) {
perror("select");
return -1;
}
if (FD_ISSET(sockfd, &wfds)) {
while (1) {
fgets(buffer, MAXDATASIZE - 1, stdin);
if ((send(sockfd, buffer, strlen(buffer), 0)) == -1) {
fprintf(stderr, "Failure Sending Message\n");
close(sockfd);
exit(1);
} else {
printf("Message being sent: %s\n", buffer);
break;
}
}
}
if (FD_ISSET(sockfd, &rfds)) {
while (1) {
if ((num = recv(sockfd, buffer, 10240, 0)) == -1) {
//fprintf(stderr,"Error in receiving message!!\n");
perror("recv");
exit(1);
} else if (num == 0) {
printf("Connection closed\n");
return 0;
}
// num = recv(client_fd, buffer, sizeof(buffer),0);
buffer[num] = '\0';
printf("Message received: %s\n", buffer);
break;
}
}
close(sockfd);
return 0;
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define BACKLOG 10 // how many pending connections queue will hold
#define MAXDATASIZE 100 // max number of bytes we can get at once
void sigchld_handler(int s) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*) sa)->sin_addr);
}
return &(((struct sockaddr_in6*) sa)->sin6_addr);
}
int main(int argc, char *argv[]) {
int sockfd; // set up socket
int new_sockfd; // new socket after connection
struct addrinfo hints, *results, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int rv;
int i;
struct timeval tv;
int num;
char buffer[10241];
int nsock;
char buf[MAXDATASIZE];
int numbytes;
char *port;
//select data
fd_set rfds;
fd_set wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
/* validate command-line arguments */
if (argc != 2) {
fprintf(stderr, "Usage: %s port\n", argv[0]);
exit(1);
}
tv.tv_sec = 100;
tv.tv_usec = 0;
/* only argument to port number (./sc [port])*/
port = argv[1];
bzero(&hints, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // any IP
if ((rv = getaddrinfo(NULL, port, &hints, &results)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for (p = results; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("1: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof (int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(results); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while (1) { // main accept() loop
sin_size = sizeof their_addr;
new_sockfd = accept(sockfd, (struct sockaddr *) &their_addr, &sin_size);
if (new_sockfd == -1) {
perror("accept");
continue;
}
//get the clients info
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *) &their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
FD_SET(new_sockfd, &rfds);
FD_SET(new_sockfd, &wfds);
if (select(new_sockfd + 1, &rfds, &wfds, NULL, &tv) < 0) {
perror("select");
return -1;
}
if (FD_ISSET(new_sockfd, &rfds))
while (1) {
if ((num = recv(new_sockfd, buffer, 10240, 0)) == -1) {
//fprintf(stderr,"Error in receiving message!!\n");
perror("recv");
exit(1);
} else if (num == 0) {
printf("Connection closed\n");
return 0;
}
// num = recv(client_fd, buffer, sizeof(buffer),0);
buffer[num] = '\0';
printf("Message received: %s\n", buffer);
break;
}
if (FD_ISSET(new_sockfd, &wfds))
while (1) {
fgets(buffer, MAXDATASIZE - 1, stdin);
if ((send(new_sockfd, buffer, strlen(buffer), 0)) == -1) {
fprintf(stderr, "Failure Sending Message\n");
close(new_sockfd);
exit(1);
} else {
printf("Message being sent: %s\n", buffer);
break;
}
}
} //Outer While
close(new_sockfd); // parent doesn't need this
return 0;
}
The host and client aren't able to communicate. Can anyone tell me where the issue is?
You need to call select() inside of the loops, not outside. Also, you have to reset the fd_set and timeval structs each time you call select().