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
}
Related
I wanted to implement a simple UDP server and client, so I wrote the code halfway in C language.
The purpose of the code is to send a message from the Client to the Server, and if the message is sent correctly, the Server sends an "ACK" message to the Client.
However, the return values of sendto() and recvfrom() are -1, and the message is not sent.
In udpserver.c, I know that "ACK" is firmly overwritten in buf by strcpy.
In udpserver.c, I can send messages and in udpclient.c, I can receive messages, but I don't know how to write code from here. Can you give me some specific code?
Also, I want the server to say Hello, and the client to say ACK.
#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 <arpa/inet.h>
#include <netdb.h>
#define MYPORT "4567" // the port that client will be connecting to
#define MAXBUFLEN 100
// 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)
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
char buf[MAXBUFLEN];
int32_t receivedNumber;
socklen_t addr_len;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
hints.ai_socktype = SOCK_DGRAM; // UDP
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, MYPORT, &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 (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "listener: failed to bind socket\n");
exit(1);
}
printf("server: waiting for client...\n");
addr_len = sizeof their_addr;
//Receive from client
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);
}
printf("Received from client: %s\n", buf);
strcpy(buf,"ACK");
//Send client an ACK message
if(sendto(sockfd,buf,strlen(buf),0,p->ai_addr,p->ai_addrlen)==-1){
printf("Error\n");
}
/*
unknown code
*/
freeaddrinfo(servinfo);
close(sockfd);
return 0;
}
#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 <arpa/inet.h>
#include <netdb.h>
#define SERVERPORT "4567" // the port that client will be connecting to
#define MAXBUFLEN 100
int main(int argc, char *argv[])
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr;
socklen_t addr_len;
int rv;
int numbytes;
char buf[MAXBUFLEN];
if (argc != 3) {
fprintf(stderr,"usage: talker hostname message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to create socket\n");
return 2;
}
// Send to server
if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0, p->ai_addr, p->ai_addrlen)) == -1) {
perror("client: sendto");
exit(1);
}
//Receive from server
if(recvfrom(sockfd,buf,MAXBUFLEN-1,0,(struct sockaddr *)&their_addr,&p->ai_addrlen)==-1){
printf("Error\n");
}
/*
unknown code
*/
printf("Received from server: %s\n", buf);
freeaddrinfo(servinfo);
close(sockfd);
return 0;
}
gcc udpserver.c
./a.out
server: waiting for client...
Received from client: Hello~
gcc udpclient.c
./a.out 127.0.0.1 Hello
Received from server: �*��z ```
If you read from a channel into a buffer and the result is positive, then you have successfully read some bytes, but not a null-terminated string.
e.g.
numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1, ...);
numbytes is the number of bytes read, now it is time to make a string:
buf[numbytes] = '\0';
Both the client and server do not add the null-byte to the end of buf.
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
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().
I think this may be a simple solution I'm just over thinking. I'm writing an extremely basic chat program where the client and server takes turns sending a message. Right now I have it where it sends a message back and forth only once and the client closes the socket. The program doesn't have to have sockets open simultaneously, just as long as it can switch back and forth like a swing, rather than a real chat program where the it can take multiple inputs from both sides all at once.
Will a while loop in the client keep it open and what are the conditions of the while loop?
I've tried several different conditions and none of them worked... it just makes it hang. I've also tried commenting out some of the close() functions in the server code, but that didn't work either.
I also have a small issue of the received input printing gibberish, but I think it's because it's printing the memory addresses of the string array when there's nothing in it... I just can't remember how to shorten it. Lol.
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 PORT "3490" // The port users will be connecting to
#define BACKLOG 10 // How many pending connections queue will hold
char input[20];
char *pointer;
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(void)
{
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;
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, 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;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // 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");
sin_size = sizeof their_addr;
while((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) > 0) { // Main accept() loop
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
char input[20];
char *pointer;
printf("Type in an server's input: ");
scanf("%s", input);
pointer = input; //Will need to clean this up to be more effcient... later
if (send(new_fd, pointer, strlen(input), 0) == -1) //Need to change the length to
//the actual length of the
//input... later.
perror("send");
close(new_fd);
exit(0);
}
char Cinput[20];
if ((recv(new_fd, Cinput, strlen(Cinput), 0)) == 0) { //NEW LINE ADDED HERE
printf("No more messages");
}
if ((recv(new_fd, Cinput, strlen(Cinput), 0)) == -1) {
perror("recv");
exit(1);
}
printf("Server: received '%s'\n",Cinput);
close(new_fd); // Parent doesn't need this
}
return 0;
}
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 PORT "3490" // The port client will be connecting to
#define MAXDATASIZE 100 // 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];
struct addrinfo hints, *servinfo, *p;
/*
This is what is in the struct
struct addrinfo {
int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc.
int ai_family; // AF_INET, AF_INET6, AF_UNSPEC
int ai_socktype; // SOCK_STREAM, SOCK_DGRAM
int ai_protocol; // Use 0 for "any"
size_t ai_addrlen; // Size of ai_addr in bytes
struct sockaddr *ai_addr; // struct sockaddr_in or _in6
char *ai_canonname; // Full canonical hostname
struct addrinfo *ai_next; // Linked list, next node
};
getaddrinfo() will return a pointer to this
*/
int rv;
char s[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"usage: client hostname\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;
}
// 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
while(1)//NEW LINE ADDEDthis is getting the client to repeat asking for the input, but doesn't send it.
{
if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == 0) { //NEW LINE ADDED
printf("Shutdown");
}
if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("client: received '%s'\n",buf);
char Cinput[20];
char *pointer;
printf("Type in an client's input: ");
scanf("%s", Cinput);
pointer = Cinput;
if (send(sockfd, pointer, strlen(Cinput), 0) == -1)
{
perror("send");
close(sockfd);
exit(0);
}
}
close(sockfd); //As soon as the client receives a message, it closes the socket.
//We probably need a while loop in here in order to keep the socket open,
//but what are the parameters for the while loop?
return 0;
}
The following pseudocode will ensure that the same client and server can infinitely send messages to each other until one of them hangs up:
Client:
Create the socket.
connect to it.
recv data.
If recv returns 0, it means the other end has performed an orderly shutdown. Go to step 7.
send response.
Go to step 3.
Stop.
Server:
Create the socket.
bind the socket to an address.
Mark the socket as listening.
accept a connection.
If accepted connection is invalid, go to step 4.
send data.
recv response.
If recv returns 0, it means the other end has performed an orderly shutdown. Go to step 4 to accept a new connection.
Go to step 6.