Hello and good evening,
I am writing a client & server pair of programs in C. Right now I got both of them running and by providing access to ports on my server through router settings, I can establish local and global connections for a barebones chat back and forth. Here is what both programs do until now.
Client:
create a socket
create an sockaddr_in (port and IP address)
connect the socket with the sockaddr_in to the server
go into a loop for read / write to the server, one at a time
Server:
create a socket
create a sockaddr_in (port and IP address)
bind the socket to sockaddr_in
listen to request on the socket
accept a request
go into a loop for read / write to the server, one at a time
My question:
What do I have to consider for handling and error-handling these individual steps correctly?
What I got until now is code like this:
printf("trying to connect to: IPv4 <%s> port <%s> ...", argv[1], argv[2]);
if(connect(net_socket, (struct sockaddr *)&server_address, sizeof server_address) == -1)
{
fprintf(stderr, "\ntcp connection failed.\n");
exit(4);
}
printf(" success.\nconnection to server: IPv4 <%s> port <%s>\n", argv[1], argv[2]);
Code like this is wrapped around all the steps these programs do from socket creation to connect and accept. And they work given the right input.
I want to scale my programs:
simultaneous read and write for both sides
multiple clients to server instead of client and server
correct data encapsulation
etc.
For that I need to know how to do the backbone correct and I want to get the basics straight before I start multithreading, forking, selecting or building whatnot around the core.
I don't want to overscale this post, hence I will only post my code if it is requested at all. Thank you very much for reading and replying in advance.
First off, it's always good practice to wrap all error/loging functions so they can be modified for later scaling. I do this so, later I can add file/line to my errors.
void perr(char *message)
{
fprintf(stderr, message);
}
On to your question. It's a very practice-rich answer. (Better asked on this board. But I'll help you anyways.
You're right to have the backbone as solid as possible before expanding. The best practice for a well made application is to have it very 'flat' in scope. (not to many if's in if's). Doing this will also provide you with a better understanding of the flow of code and more atomic error reporting:
main()
{
if(!(soc = createsocket(...)) return 1;
if(!bindsocket(soc))
{
deletesocket(soc);
return 1;
}
// Main server loop
while(req = accept_request(soc))
{
proc_request(req);
}
plog("No longer accepting request");
return 0;
}
Notice how I have no error reporting in the driver function (main in this example). This is because all error reporting is handled INSIDE of the called functions (createsocket(), bindsocket(), ect).
The driver function is only concerned if the called functions succeeded or not. The details of each failure are best described in the function who's arguments lead to the failure.
Here are my tips:
Avoid exit(), your code needs to be ready to use return all the way up to main() if something goes wrong. There are execptions (threads and child procs, but you get the idea)
Good programs are good at failing
For a server/client relationship, the server is typically single threaded and only loops through all client requests (computers are faster than you think, unless you're expecting around 5k requests).
State Machines are extremely useful in your situation. Think of the states your program is going to be in between start and stop, lay out what methods need to be in each state and have an overall plan what happens with errors in each state. (ie, opened, establishing, running, stopping)
Related
For connecting to a web server I use below code in C:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define PORT 4455
void main()
{
int clientSocket;
struct sockaddr_in serverAddr;
char buffer[1024];
clientSocket=socket(PF_INET,SOCK_STREAM,0) ;
printf("[+]client socket created successfully.\n");
memset(&serverAddr,'\0',sizeof(serverAddr));
serverAddr.sin_family=AF_INET;
serverAddr.sin_port=htons(PORT);
serverAddr.sin_addr.s_addr=inet_addr("https://www.google.com/");
connect(clientSocket,(struct sockaddr*)&serverAddr,sizeof(serverAddr));
printf("[+]connected to the server\n");
recv(clientSocket,buffer,1024,0);
printf("[+]data received is %s\n",buffer);
printf("[+]closing the connection\n");
}
But when I compile and run the code, it doss not receive any data, or the received data is not valid.
For example, received data from Google is :
��5��
How can I fix it?
You're not checking the return values from your networking function calls, so you have no idea whether they succeeded or failed. It's possible that all your calls failed, and your code is just printing the random data in the uninitialized array buffer.
In addtion, you're doing a lookup for https://google.com, which is a URI, not an address. Most likely inet_addr() is not the right function to resolve a name to an IP address, even if you had an IP address, which you don't. You probably need to start with gethostbyname().
Most importantly, even if you do manage to make a network connection to Google's HTTPS service, you'll be dealing with encrypted data, so you won't be able to process it unless you set up a encrypted TLS session. That's a long way from being straightforward, and most people use libraries like OpenSSL for this kind of thing.
I suspect that some of the problems I've noticed could have been spotted by your C compiler, if you turned its warning level up.
If you want to practice socket programming, I'd recommend that you start with a server that accepts simple, plaintext communication. If you want to practice socket programming with encryption, I'd recommend that you investigate OpenSSL or some similar library, or settle in for a lot of hard work. If you just want to make HTTP requests from your application, whether encrypted or not, I'd suggest looking at library like libcurl, which makes this kind of thing very easy.
There are loads and loads of C socket programming examples, on SO and many other places, if you need inspiration.
Consider the following implementation of a TCP server written in C:
struct sockaddr_in client;
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
// bind, listen, error handling, etc.
while(1) {
int client_fd = accept(server_fd,
(struct sockaddr *) &client,
&client_len);
if (client_fd < 0) {
exit(1);
}
// do something with client_fd
close(client_fd);
}
Is it possible for a client to deliberately crash this server by sending packets such that the accept call fails (returns a value less than zero)?
The man-page (Debian 10) lists errors like the following but I'm not sure if a client is able to trigger any of them:
In addition, network errors for the new socket and as defined for the protocol may be returned.
Various Linux kernels can return other errors such as ENOSR, ESOCKTNOSUPPORT, EPROTONOSUPPORT,
ETIMEDOUT. The value ERESTARTSYS may be seen during a trace.
EPROTO Protocol error.
I've already tried several rather simple things, like disconnecting the client socket immediately. My next step to approach this problem would be to craft some packets manually, using RAW sockets or a tool like Scapy and then trying to find a way to trigger a protocol error.
I couldn't find any information on this so my question is if it's even possible to deliberately do it or if I can stop trying.
There are many ways for accept() to fail, and they are not entirely standardized across operating systems and socket implementations.
It is not a good idea to terminate your server just because one accept() call failed. Better to log an error and wait for the next incoming connection. A handful of errors like EINVAL, EFAULT and ENOTSOCK might be considered non-recoverable in the example you gave, so you could make the server exit if it gets one of those, but it should be considered carefully in the larger context of your application stack (e.g. if there is a watchdog that will restart the server automatically).
Applying what I learned in school to a hobby project, but I'm new to network programming. I'm designing a simple multi-user text game with a C server on Linux. I have a simple threaded C echo server using NPTL (Native POSIX Threads Library) for incoming connections. I'd like to avoid race and deadlock conditions by opening a new file descriptor each connection so they can access database items for equipment and so forth without having to use locks. Would I be able to use select() to poll each of my threaded connections? Does this make sense for best practices?
I'm trying to find an example in Beej's Guide to Network Programming but I'm coming up short.
Edit:
Here's the basics of my echo server:
// .. pick a port on startup
// initial socket creation
lsock = socket(AF_INET,SOCK_STREAM,0);
// ... error handling
sname.sin_family = AF_INET;
sname.sin_addr.s_addr = INADDR_ANY;
sname.sin_port = htons( port );
// binding the socket
while(client_sock=accept(lsock,(struct sockaddr*)&client,(socklen_t*)&c))
{
puts("New connection accepted");
pthread_t cthread;
new_sock = malloc(1);
*new_sock = client_sock;
if( pthread_create( &cthread , NULL , clientHandler , (void*) new_sock) < 0)
{
perror("Error: Thread creation");
return 1;
}
puts("Handler assigned");
}
void *clientHandler(void *socket_desc)
{
//Get the socket descriptor
int sock = *(int*)socket_desc;
int n;
char sendBuff[100], client_message[2000];
while((n=recv(sock,client_message,2000,0))>0)
{
send(sock,client_message,n,0);
}
close(sock);
if(n==0)
{
puts("Client disconnected");
}
else
{
perror("recv failed");
}
return 0;
}
Here's what my plans were to implement ontop of my echo server on a basic room system and player system below:
struct pollfd pdata[MAXPLAY+1];
struct _player{
obj o;
int id,state,file_desc,roomNum;
void(*ip)(plr *p, char ch);
char name[16],op[MAXBUF],line[128];
struct pollfd *p;
int HP,ATK;
plr *target;
};
void init_player(player *p){
p->HP=10;
p->ATK=1;
p->target=NULL;
}
#define playerLloop(p) for(i=0,p=players;i<MAXPLAY;i++,p++)
if(p->state==0){
init_plr(p);
p->state=1;
p->name[0]=0;
p->file_desc=rsock; // file descriptor
p->roomNum=0;
// function to initialize names and add to linked list of players
p->ip=new_connect;
// function to send clients a message
sendp(p,"Welcome");
}
Once I initialize a player through my client handler, I'd add them into a linked list with their file descriptor. My question would come in here; what's the best way to avoid any type of race or deadlock condition when I have two separate threads trying to access an object on the server? Could I resolve this by handling it through my parsing function to get client input?
If player one tries to open a door in a room, player two tries to open the same door; when I read the file descriptor for both clients through the threaded connections, how do I assign priority to those sockets? Could this potentially cause a race or deadlock condition?
I'm just having a hard time understanding the concept of reading information across a threaded socket.
Would I be able to use select() to poll each of my threaded connections?
select() is for use in serving multiple I/O channels efficiently via a single thread. You clarified in comments that you intend to have a separate thread for each client. In that case, you do not need select to manage the client connections: each such connection has a whole thread dedicated to it!
You seem to be proposing that each of those threads would additionally obtain all the other resources it needs, too, such as connections to the underlying database. In that case, yes, to the extent that those external resources inherently support the kind of concurrent access you need, you can avoid using locks in your own code to protect them.
Does this make sense for best practices?
The thread-per-client model is a reasonably common one. It should be fine as long as the number of concurrent clients is not too large. You will want to ensure that there is a way to clean up the threads of clients that disappear without notice. How large is "too large" depends on details of the work each thread actually performs, and on the characteristics of the machine on which this runs.
If the number of concurrent clients grows large enough then you will need to change your approach, but I don't think you need to worry too much about that just yet. Keeping your code clean and modular and avoiding reliance on thread-local data will facilitate switching to a different approach if ever you reach the point where you need to do.
i've read here about this topic in a lot of differents ways, and i want to know whats the best practices of "creating a Linux TCP server with C and Multithreading".
so far i've read :
1-Duplicating process, with Fork().
2-Creating separated threads for each client. multithread server/client implementation in C
3-Creating Asynchronous threads for each connection
i've read that Fork and thread for each connection are not best practices, but, im not sure what really is one?
i have a small server with asynchronous threads for each connection and i have problems with bind() in the time, if i kill the process and start it again, it need like 5 minutes to start again, because i get " ERROR on binding: Address already in use " and i decided to fix it, but with the best practices.
many thanks in advance and sorry for my english .
Regarding your problem binding..
Set the option SO_REUSEADDR to enable binding to a port already in use (under certain circumstances). Set it before bind.
now it will work fine
...
servSock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
int optval = 1;
setsockopt(servSock,SOL_SOCKET,SO_REUSEADDR,(void *)&optval,sizeof(optval));
/* Construct local address structure */
memset(&echoServAddr,0,sizeof(echoServAddr)); /* Zero out the structure */
echoServAddr.sin_family=AF_INET; /* Internet address family*/
echoServAddr.sin_addr.s_addr=htonl(INADDR_ANY); /* Any incoming interface */
echoServAddr.sin_port = htons(echoServPort); /* Local port */
/* Bind to the local address */
bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr));
...
Is obsolete since the introduction of threads.
This is the most widely used technique.
You've misread this. You can use asynchronous I/O, but it's a complex programming model and not to be entered into lightly.
You left out non-blocking I/O with select(), poll(), epoll().
If you know what you're doing and you expect very high load you should investigate 3 or 4. Otherwise you should start with 2, as it's the easiest to program and get working, and see by observing it in production whether you have a capacity problem. Odds are than you will never need to progress beyond this model.
I would suggest you to read the doc & code of libev, that is state-of-the-art.
I am totally new to socket programming and I want to program a combined TCP/UDP-Server socket in C but I don't know how to combine those two.
So at the moment, I do know how TCP- and UDP-Server/-Clients work and I have already coded the Clients for TCP and UDP. I also know that I have to use the select()-function somehow, but I don't know how to do it.
I have to read two numbers, which are sent to the TCP-/UDP-Server with either TCP- or UDP-Clients and then do some calculations with these numbers and then print the result on the server.
Does anyone know a tutorial for that or an example code or can help me with that?
Or at least a good explanation of the select() function.
Basically, use an event loop. It works like this:
Is there anything I need to do now? If so, do it.
Compute how long until I next need to do something.
Call select specifying all sockets I'm willing to read from in the read set and all sockets I'm trying to write to in the write set.
If we discovered any sockets that are ready for reading, read from them.
If we discovered any sockets that are ready from writing, try to write to them. If we wrote everything we need to write, remove them from the write set.
Go to step 1.
Generally, to write to a socket, you follow this logic:
Am I already trying to write to this socket? If so, just add this to the queue and we're done.
Try to write the data to the socket. If we sent it all, we're done.
Save the leftover in the queue and add this socket to our write set.
Three things to keep in mind:
You must set all sockets non-blocking.
Make sure to copy your file descriptor sets before you pass them to select because select modifies them.
For TCP connections, you will probably need your own write queue.
The idea is to mix inside your server a TCP part and a UDP part.
Then you multiplex the inputs. You could use the old select(2) multiplexing call, but it has limitations (google for C10K problem). Using the poll(2)
multiplexing call is preferable.
You may want to use some event loop libraries, like libev (which uses select or poll or some fancier mechanisms like epoll). BTW, graphical toolkits (e.g. GTK or Qt) also provide their own even loop machinery.
Read some good Linux programming book like the Advanced Linux Programming
book (available online) which has good chapters about multiplexing syscalls and event loops. These are too complex to be explained well in a few minutes in such an answer. Books explain them better.
1) Simple write a tcp/udp server code, and when receive the message, just print it out.
2) substitute print code to process_message() function.
Then you have successfully combine TCP and UDP server to the same procedure.
Be careful with your handling procedure, it's should be cope with parellel execution.
You may try this stream_route_handler, it is c/c++ application, you can add tcp/udp handler in your single c/c++ application. This has been using by transportation heavy traffic route, and logging service purpose.
Example of using
void read_data(srh_request_t *req);
void read_data(srh_request_t *req) {
char *a = "CAUSE ERROR FREE INVALID";
if (strncmp( (char*)req->in_buff->start, "ERROR", 5) == 0) {
free(a);
}
// printf("%d, %.*s\n", i++, (int) (req->in_buff->end - req->in_buff->start), req->in_buff->start);
srh_write_output_buffer_l(req, req->in_buff->start, (req->in_buff->end - req->in_buff->start));
// printf("%d, %.*s\n", i++, (int) (req->out_buff->end - req->out_buff->start), req->out_buff->start);
}
int main(void) {
srh_instance_t * instance = srh_create_routing_instance(24, NULL, NULL);
srh_add_udp_fd(instance, 12345, read_data, 1024);
srh_add_tcp_fd(instance, 3232, read_data, 64);
srh_start(instance);
return 0;
}
If you are using C++ program, you may like this sample code.
stream route with spdlog