How to call a function in a C server from a client - c

I made an application in C that adds structs to a file and now I want to make it work trough sockets, making the client ask the user to submit the fields of the struct and then the server saves it in the file saving a log file for each action made by the client (add,delete,search,etc). How can I do this? also is it possible to call a function residing in the server from the client?
Thanks in advance.

Create socket listener at server and wait for client connection.
When client sends you something, parse it and make the appropriate action.
for example:
/* Create the socket and listen to cennections */
sock = create_socket(port);
if (listen(sock, 1) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
/* Never end loop for socket listen */
while(1)
{
/* Accept socket */
newsock = accept(sock, (struct sockaddr *) &peer, &len);
/* dump_sockaddr (peer, len); */
if (newsock >= 0)
{
/* Null-terminate socket buffer */
bzero(buf, MAXMSG);
/* Read from socket */
ret = read(newsock, buf, sizeof(buf));
if (ret > 0)
{
printf("Recieved command: %s\n", buf);
/* Parse command from buffer */
if (strstr(buf, "deletesomethig") != NULL)
{
/* Make the appropriate action */
delete_something();
}
else if (strstr(buf, "stopsomething") != NULL)
{
/* Make the appropriate action */
stop_something();
}
else
printf("Recieved unsupported command: %s\n", buf);
/* Close socket */
close(newsock);
}
}
}
/* Close socket */
close(sock);
Just be sure that strstr() has no trouble with memory. This is simple example, you can use sscanf() for parsing commands from socket buffer.

Related

Open and close socket when not using for communication, only using select() in language C

I'm writing a peer 2 peer chat application using TCP. This application includes client and server part in one file. I'm using select() without using fork(), pthread to handle connections. This is my mechanism of application, I run the application on a same host by running application on different terminals:
Initialize the first node, let say P1, as ./p2p portToListen.
This node will open a socket on portToListen, let say ServerSock1, to listen connections from other peers.
And then it goes to a while loop with a select() function to wait for events.
Initialize the second node, let say P2, as ./p2p portToListen portToConnectTo (I don't specify IP address here since I'm running on a local machine).
This also opens a new socket, let say ServerSock2, on "portToListen" as the first one, opens a new socket, let say ClientSock2 to connect to the first one. And then goes into the while loop.
When P2 connects to P1, at P1, it also accepts a new connection with a socket, ClientSock1.
After some phases for setting network information and exchanging configuration of the network(P1 sends information of current group to P2 and waits for ACK from P2), they are able to send chatting messages.
My question is, in this case, do I need to close the socket every time a peer sends/receives configuration information(not chatting message). For example, after P1 sends information of current group, do I need to close both ServerSock1 and ClientSock1, and after P2 sends ACK, do I need to close both ServerSock2 and ClientSock2? I think, ServerSock1,2 should always be opened? And only be closed outside of while loop?
If I do that, select() will not work since there is no socket to monitor events. When two peers would like to send chatting message, they need to open sockets again for Server side and Client side, send information to set up a new connection, by calling (socket(), bind(), listener(), accept(), connect()), send some chatting messages and close() sockets again.
Furthermore, if I want to send a broadcasting messages to other peers in a same group, I need to open sockets of peers again, send a chatting message and close() every socket?
In general, since I'm using only select(), what is a correct way to close() and open sockets? If it's possible, can you give me a general scenario for this? I really appreciate for your any comments. Thanks very much. Below is my general code:
int main(void)
{
int sock;
fd_set socks;
fd_set readsocks;
int maxsock;
int reuseaddr = 1; /* True */
struct addrinfo hints, *res;
/* Get the address info */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
perror("getaddrinfo");
return 1;
}
/* Create the socket */
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock == -1) {
perror("socket");
return 1;
}
/* Enable the socket to reuse the address */
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1) {
perror("setsockopt");
return 1;
}
/* Bind to the address */
if (bind(sock, res->ai_addr, res->ai_addrlen) == -1) {
perror("bind");
return 1;
}
freeaddrinfo(res);
/* Listen */
if (listen(sock, BACKLOG) == -1) {
perror("listen");
return 1;
}
/* Set up the fd_set */
FD_ZERO(&socks);
FD_SET(sock, &socks);
FD_SET(0, &socks);
maxsock = sock;
if (argc > 2)
{
clientSock2 = ConnectToServer(IPaddres, portToConnect);
FD_SET(clientSock2, &socks);
}
/* Main loop */
while (1) {
unsigned int s;
readsocks = socks;
if (select(maxsock + 1, &readsocks, NULL, NULL, NULL) == -1) {
perror("select");
return 1;
}
for (s = 0; s <= maxsock; s++) {
if (FD_ISSET(s, &readsocks)) {
printf("socket %d was ready\n", s);
if (s == sock) {
/* New connection */
int clientSock1;
struct sockaddr_in their_addr;
size_t size = sizeof(struct sockaddr_in);
clientSock1 = accept(sock, (struct sockaddr*)&their_addr, &size);
if (newsock == -1) {
perror("accept");
}
else {
printf("Got a connection from %s on port %d\n",
inet_ntoa(their_addr.sin_addr), htons(their_addr.sin_port));
FD_SET(clientSock1, &socks);
if (clientSock1 > maxsock) {
maxsock = clientSock1;
}
}
}
else {
/* Handle send, recv() information of network */
handle(s, &socks);
}
}
}
if (FD_ISSET(0, &readset) { // Handle input
// Sending chatting message
}
}
close(sock);
return 0;
}
You should reuse the same connection. You may need to adjust your protocol so you know exactly where a request and a response finish and a new one starts.
No, you don't have to close() anything unless you want to terminate a connection or stop listening for new ones.
The listen() socket will continue to listen for new connection attempts even after you accept() one, just keep checking for new connections with select() and accept() them aswell, you don't have to recreate this socket.
An estabilished TCP session is a two-way stream of data where you can safely send() and recv() multiple times as long as you need to communicate with that peer. Reconnecting after each block of information is pointless.
Use select() in the accepted connections too to check if there is information ready to recv().
Note you have to rebuild the fd_set every time you call select() on it. The way your code it doing it is wrong.

how can we send a text file from client to the server?

I am trying to send a file from client to the server using C socket programming. but in the server side I am not able to receive the file which I had sent from the client. I am attaching the codes below.
server:
/* Create a connection queue and wait for clients. */
listen(server_sockfd, 5);
while(1) {
char ch;
printf("server waiting\n");
/* Accept a connection. */
client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_address,cli);
if(client_sockfd > 0)
printf("client is connected\n");
/* We can now read/write to client on client_sockfd. */
char *fh;
recv(client_sockfd,fh,1024+1,0);
printf("server recieved %s",fh);
/* read(client_sockfd, &ch, 1);
ch++;
write(client_sockfd, &ch, 1); */
return close(client_sockfd);
}
}
You need to check the return of recv
if ((nbytes = recv(client_sockfd,fh,1024+1,0)) > 0)
and end your buffer with '\0'
fh[nbytes] = '\0';
printf("server recieved %s",fh);
Also, is not a good idea to use magic numbers like 1024+1

Linux TCP server, sending RAW data to few clients every X seconds

I'm writing simple TCP server and I found some issue. Maybe you can help me a bit.
So, I wrote an echo server first (to test connection with computer client). It's working okay, but now I need to change it a bit. Server should sent char[100] to client when it connects and sent same char[] to every client every X seconds/minutes.
I've trying many changes, but application only crashes. Commented some of my "mistakes" in this code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
/* BufferLength is 100 bytes */
#define BufferLength 100
/* Server port */
#define SERVPORT 6000
int main(){
/* Variable and structure definitions. */
int sd, wyslij, sd2, rc, length = sizeof(int);
int totalcnt = 0, on = 1;
char temp;
char buffer[BufferLength];
struct sockaddr_in serveraddr;
struct sockaddr_in their_addr;
fd_set read_fd;
struct timeval timeout;
timeout.tv_sec = 15;
timeout.tv_usec = 0;
char datadata[100] = "This is a test string from server lol!!! ";
/* Get a socket descriptor */
if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("Server-socket() error");
/* exit */
exit (-1);
}else
printf("Server-socket() is OK\n");
/* Allow socket descriptor to be reusable */
if((rc = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0){
perror("Server-setsockopt() error");
close(sd);
exit (-1);
}else
printf("Server-setsockopt() is OK\n");
/* bind to an address */
memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVPORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("Using %s, listening at %d\n", inet_ntoa(serveraddr.sin_addr), SERVPORT);
if((rc = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0){
perror("Server-bind() error");
/* Close the socket descriptor */
close(sd);
/* and just exit */
exit(-1);
}else
printf("Server-bind() is OK\n");
/* queue up to 10 clients */
if((rc = listen(sd, 10)) < 0){
perror("Server-listen() error");
close(sd);
exit (-1);
}else
printf("Server-Ready for client connection...\n");
/* accept() the incoming connection request. */
int sin_size = sizeof(struct sockaddr_in);
if((sd2 = accept(sd, (struct sockaddr *)&their_addr, &sin_size)) < 0){
perror("Server-accept() error");
close(sd);
exit (-1);
}else
printf("Server-accept() is OK\n");
/*client IP*/
printf("Server-new socket, sd2 is OK...\n");
printf("Got connection from the client: %s\n", inet_ntoa(their_addr.sin_addr));
/* Wait for up to 15 seconds on */
/* select() for data to be read. */
FD_ZERO(&read_fd);
FD_SET(sd2, &read_fd);
rc = select(sd2+1, &read_fd, NULL, NULL, &timeout);
/* rc = write(sd2, datadata, sizeof(datadata)); */
if((rc == 1) && (FD_ISSET(sd2, &read_fd))){
/* rc = write(sd2, datadata, sizeof(datadata)); */
/* Read data from the client. */
totalcnt = 0;
while(totalcnt < BufferLength){
/* read() from client */
rc = read(sd2, &buffer[totalcnt], (BufferLength - totalcnt));
if(rc < 0){
perror("Server-read() error");
close(sd);
close(sd2);
exit (-1);
}else if (rc == 0){
printf("Client program has issued a close()\n");
close(sd);
close(sd2);
exit(-1);
}
else{
totalcnt += rc;
printf("Server-read() is OK\n");
}
}
}else if (rc < 0){
perror("Server-select() error");
close(sd);
close(sd2);
exit(-1);
}
/* rc == 0 */
else{
printf("Server-select() timed out.\n");
close(sd);
close(sd2);
exit(-1);
}
/* Shows the data */
printf("Received data from the client: %s\n", buffer);
/* write() some bytes of string, */
/* back to the client. */
printf("Server-Echoing back to client...\n");
rc = write(sd2, datadata, sizeof(datadata));
if(rc != totalcnt){
perror("Server-write() error");
/* Get the error number. */
rc = getsockopt(sd2, SOL_SOCKET, SO_ERROR, &temp, &length);
if(rc == 0){
/* Print out the asynchronously */
/* received error. */
errno = temp;
perror("SO_ERROR was: ");
}else
printf("Server-write() is OK\n");
close(sd);
close(sd2);
exit(-1);
}
/* Close the connection to the client and */
/* close the server listening socket. */
close(sd2);
close(sd);
exit(0);
return 0;
}
Thanks a lot buddies!
You may want to check out D.J. Bernstein's tcpserver (see http://cr.yp.to/ucspi-tcp/tcpserver.html). Basically, you can simply run your C program under tcpserver, and tcpserver will handle everything as far as setting up the sockets, listing for incoming connections on whatever port you are using, etc. When an incoming connection arrives on the port that you specify, tcpserver will spawn an instance of your program and pipe incoming info from the client to your program's STDIN, and pipe outgoing info from your program's STDOUT back to the client. This way, you can concentrate on your program's core logic (and simply read/write to stdout/stdin), and let tcpserver handle all of the heavy lifting as far as the sockets, etc.
Well, I ran your program against a simple TCP client code and did not see any crash. So, you probably should add gdb info to that. Also, in the program, I don't see where your programs wakes up periodically (you do have a comment) and sends data to the client. You should also consider adding the client fd to the list of read fd set and have one common select() call. If the select() returns a read-event on the listener, then that is a new connection and you should call accept. If the select() returns a read-event on a child fd, then you have some data to read adn you should call recv()/read().

Trying to accept a socket connection, retrieve and process a message, and then write this message to the terminal

I need to write a program to retrieve and process a message through a socket. I have no trouble and understand the process of creating the socket, binding the socket, listening for the incoming connection and then accepting it. I have trouble with receiving the message from the file stream that I have created for the accepted connection. My code is below (I've included pretty much all of it for reference but I think the problem comes after the connection is accepted..):
int sockfd; // the socket file descriptor
int conn_fd; // the accepted connection file descriptor
FILE *conn_fs; // the file stream for the accepted connection
char *conn_buff;
//char conn_buff[LINE_MAX]; // a buffer to store the data received from the accepted connection
int main(int argc, char *argv[]) {
int port = 0;
if ((argc != 2) || (sscanf(argv[1], "%d", &port) != 1)) {
fprintf(stderr, "Usage: %s [PORT]\n", argv[0]);
exit(1);
}
if (port < 1024) {
fprintf(stderr, "Port must be greater than 1024\n");
exit(1);
}
struct sockaddr_in servaddr; // socket address structure (socket in address)
// set all bytes in socket address structure to zero, and fill in the relevant data members
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port); // port is the first argument given in the terminal
// tell the user the servaddr structure has been initialised.
//printf("servaddr structure has been initialised..\n\ncreating socket..\n");
// create the socket using IPv4 (AF_INET) and 2-way comms (SOCK_STREAM)
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { // socket() returns < 0 if an error occurs
printf("error: creating the socket..\nexitting..\n");
exit(EXIT_FAILURE);
}
// tell the user the socket has been created
//printf("socket created..\n\nbinding socket..\n");
// bind the socket to the socket address structure defined above
if (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { // bind() returns < 0 if an error occurs
printf("error: binding the socket..\nexitting..\n");
exit(EXIT_FAILURE);
}
// tell the user the socket is "bound"
//printf("socket is now \"bound\"..\n\nmarking the socket as passive..\n");
// set the socket to accept incoming connections (listen)
if (listen(sockfd, SOMAXCONN) < 0) { // listen() returns < 0 if an error occurs
printf("error: marking the socket as a passive socket in the function listen()..\nexitting..\n");
exit(EXIT_FAILURE);
}
// tell the user the socket is listening for incoming connections
printf("socket is now passive (listening)..\n\naccepting incoming connections..\n");
// accept an incoming connection
if (conn_fd = accept(sockfd, NULL, NULL) < 0) { // accept() returns < 0 if an error occurs
printf("error: accepting an incoming connection..\nexitting..\n");
exit(EXIT_FAILURE);
}
// tell the user a connection has been accepted
printf("a connection has been accepted..\n\ncreating a file stream for this connection..\n");
/*
// open a file stream associated with conn_fd
if ((conn_fs = fdopen(conn_fd, "r+")) == NULL) { // fdopen() returns NULL if an error occurs
printf("error: associating a stream to the connections file descriptor in the function fdopen()..\nexitting..\n");
exit(EXIT_FAILURE);
}
// echo out the incoming message
/*while (fgets(conn_buff, LINE_MAX, conn_fd) != NULL) { // fill the conn_buff buffer with data from the file stream conn_fs. returns NULL on error or EOF
//while (read(conn_fd,conn_buff,sizeof(char)*LINE_MAX) > 0) {
printf(conn_buff);printf("OH HAIIIIIIIIIIIIIIIIIIIIIIIIII\n");
} printf("OH HAI\n");*/
size_t len = 100;
ssize_t read;
while ((read = getline(&conn_buff, &len, conn_fs)) != -1) {
printf("Retrieved line of length %zu :\n", read);
printf("%s", conn_buff);
}
free(conn_buff);
// check if there was an error
if (ferror(conn_fs) != 0) { // returns nonzero if the error indicator is set for conn_fs (eg, if fgets() above returned NULL due to an error)
printf("error: an error occurred whilst retrieving data from the stream..\nexitting..\n");
exit(EXIT_FAILURE);
}
// close the file stream associated with conn_fd
if (close((int) conn_fs) != 0) { // close the file stream
printf("error: closing the file stream..\nexitting..\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
When I run the program, it stops inside the while ((read = getline(... loop. If I hit enter to flush the buffer then I get "Retrieved line of length 1 : " from the terminal. I figured out that the program is (for some reason I cannot understand) reading from stdin. It waits for me to type anything and as soon as I flush the buffer (ie hit enter) it sends it all back to me. The only way to close the program is using Ctrl+C. For this reason I think there is a problem with my if ((conn_fs = fdopen(conn_fd, "r+")) == NULL) { statement.
I have tried MANY different ways of reading from the stream (fread(), read(), getline(), fgets(), fgetc()...) and they all had the same effect. Then when I realised the problem must be with how I open the file stream, I tried using fopen() but to no avail.
I'm probably making a stupid mistake but can anyone help me with this please?
The client is never closing the connection.

closing socket in BSD sockets

My code is :
int main(int argc, char *argv[])
{
int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */
struct sockaddr_in my_addr; /* my address information */
struct sockaddr_in their_addr; /* connector's address information */
socklen_t sin_size;
/* generate the socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
/* generate the end point */
my_addr.sin_family = AF_INET; /* host byte order */
my_addr.sin_port = htons(MYPORT); /* short, network byte order */
my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
/* bzero(&(my_addr.sin_zero), 8); ZJL*/ /* zero the rest of the struct */
/* bind the socket to the end point */
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \
== -1) {
perror("bind");
exit(1);
}
/* start listnening */
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
printf("server starts listnening %d...\n",sockfd);
/* repeat: accept, send, close the connection */
/* for every accepted connection, use a sepetate process or thread to serve it */
while(1) { /* main accept() loop */
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \
&sin_size)) == -1) {
perror("accept");
continue;
}
printf("server: got connection from %s\n", \
inet_ntoa(their_addr.sin_addr));
if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) {
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("Received: %s",buf);
if (send(new_fd, "Hello, world!\n", MAXDATASIZE, 0) == -1)
perror("send");
close(new_fd);
exit(0);
close(new_fd); /* parent doesn't need this */
while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */
}
return 0;
}
So whenever I execute this server, after one client uses that it terminates. But If I want to execute it again lets say within 60 seconds, then it gives an error of bind: Address already in use I thought the close() function actually releases the socket so that it would be available to use it again instantly. So what am I missing here?
Before calling bind, you can mark that you want to potentially reuse an address/port using the SO_REUSEADDR socket option:
int reuseaddr = 1;
int err = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
&reuseaddr, sizeof(reuseaddr));
Also, I don't see where BACKLOG is defined, which you use in the listen() call. If this is by chance set to 1, you may want to increase it. Then, while the last socket closes, you can be handling the next call.
Firstly the original form of this code comes from Beej's guide
You have supplied code which is either very wrong or edited for brevity. After sending the "Hello World" response you call exit(0); Please add curly braces.
if (send(new_fd, "Hello, world!\n", MAXDATASIZE, 0) == -1)
perror("send");
close(new_fd);
exit(0);
Beej;s code:
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
if (send(new_fd, "Hello, world!", 13, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this`
May I also point out that Beej's code and yours does not handle the event where 'recv' returns 0 in the case a connection was lost or aborted by the client. On a side note remember a call to recv will block.
if ((numbytes=recv(new_fd, buf, MAXDATASIZE, 0)) == -1) {
perror("recv");
exit(1);
}
While this seems it probably wont affect the crash this particular issue may cause unexpected crashes later when the client is closed unexpectedly.
Delay is due to TIME_WAIT
In the process of terminating a connection, the important thing to keep in mind is that the application process on both sides of the connection must independently close its half of the connection. Due to the Three Way Handshake policy of a TCP connection,kernel waits for the acknowledgment that the connection on the other side is also closed
However, You can override this functionality by following methods:
Method 1
In the /etc/sysctl.conf file, add the following lines to persist it after reboot:
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
Method 2
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse

Resources