A Socket Example & George Mason University
Everything is a file descriptor Most socket system calls operate on file descriptors Server - Quick view socket() bind() listen() accept() send(), recv() close() - setup the file descriptor - setup socket comm parameters - wait for incoming connections - process incoming connections - send and receive data - close/terminate connection
int socket(int domain, int type, int protocol); domain = PF_INET or PF_INET6 type = SOCK_STREAM or SOCK_DGRAM protocol = 0 choose the proper protocol for the given type return = the socket file descriptor
Getting a TCP socket file descriptor socket(pf_inet, SOCK_STREAM, 0); Getting a UDP socket file descriptor socket(pf_inet, SOCK_DGRAM, 0);
(sockfd, struct sockaddr *my_addr, int addrlen); () socket sockfd = socket file descriptor returned by my_addr = pointer to struct of type sockaddr addrlen = the size of the above struct
my_addr.sin_family = AF_INET; my_addr.sin_port = htons(1234); // short, network byte order my_addr.sin_addr.s_addr = INADDR_ANY; memset(my_addr.sin_zero, '\0', sizeof(my_addr.sin_zero)); bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
int listen(int sockfd, int backlog); () socket Sockfd = socket file descriptor returned by Backlog = the number of connections allowed on the incoming queue Example listen(sockfd, 10);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); () select sockfd = socket file descriptor returned by addr = pointer to struct of type sockaddr addrlen = pointer to an int equal to the size of the addr struct Return = the file descriptor of this connection
int new_fd, addr_size; struct sockaddr_in their_addr; new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
int send(int sockfd, const void *msg, int len, int flags); () select sockfd = socket file descriptor returned by msg = the buffer with the data we want to send len = the size of the msg buffer flags = additional flags return = the number of bytes sent
int recv(int sockfd, void *buf, int len, unsigned int flags); sockfd = () select socket file descriptor returned by buf = the buffer to store the data len = the size of the above buffer flags = additional flags return = the number of bytes received
int close(int sockfd); sockfd = the file descriptor associated with the connection we want to terminate
Some functions block while waiting for an event accept() blocks while waiting for a new connection recv() blocks while waiting for new data to arrive We can not use two or more blocking functions on the same process.
() clone Using fork() or One process is responsible for accepting new connections For every connection we create a new process to send() and recv() data () select Using Monitor all sockets for reading and writing When a socket is ready for reading you can call a blocking function on that socket and it is guaranteed that recv() is not going to block
(;;) for { /* accept blocks until we have a new connection */ client_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_addrlen); /* we save the file descriptor on global array all_fds[] */ all_fds[client_fd] = client_fd; } /* we create a new process that will handle this connection */ clone(handle_client, (void **) (malloc(0x10000) + 0xFFFC), CLONE_FILES, (void *)client_fd);
() fork (. etc Sets up a default environment (stack, file descriptor table () fork Execution continues from the point where we called () clone We have control over the forking environment Share resources (like file descriptor table) between processes Execution continues from a user specified function
(;;) for { select(fd_setsize, &read_fds, NULL, NULL, NULL); (++ fd for (fd = 0; fd < FD_SETSIZE; { (( read_fds & if (FD_ISSET(fd, { read(fd, data, 512); (++ i for (i = 0; i < FD_SETSIZE; { ( listen_fd if (FD_ISSET(i, &all_fds) && i!= fd && i!= write(i, data, data_size); } } } }
Multi-process Server and Client using clone() and select() http://cs.gmu.edu/~astavrou/courses/cs_571_f09/cprocesstutorial