Creation of socket-based connections requires several operations.
First, a socket is created with socket.
Second, bound to an address with bind. Next, a willingness to accept incoming connections and a queue limit for incoming connections are specified with listen.
Finally, the connections are accepted with accept.
Accept a connection on a socket
accept() extracts the first connection request on the queue of pending connections, creates a new socket with the same properties of socket, and allocates a new file descriptor for the socket.
If no pend-ing connections are present on the queue, and the socket is not marked as non-blocking, accept() blocks the caller until a connection is present.
Code
/*
* tcpserver.c - A simple TCP echo server
* usage: tcpserver <port>
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 1024
/*
* error - wrapper for perror
*/
void error(char *msg) {
perror(msg);
exit(1);
}
int main(int argc, char **argv) {
int parentfd; /* parent socket */
int childfd; /* child socket */
int portno; /* port to listen on */
int clientlen; /* byte size of client’s address */
struct sockaddr_in serveraddr; /* server's addr */
struct sockaddr_in clientaddr; /* client addr */
struct hostent *hostp; /* client host info */
char buf[BUFSIZE]; /* message buffer */
char *hostaddrp; /* dotted decimal host addr string */
int optval; /* flag value for setsockopt */
int n; /* message byte size */
/*
* check command line arguments
*/
if (argc != 2) {
fprintf(stderr, “usage: %s <port>\n”, argv[0]);
exit(1);
}
portno = atoi(argv[1]);
/*
* socket: create the parent socket
*/
parentfd = socket(AF_INET, SOCK_STREAM, 0);
if (parentfd < 0)
error("ERROR opening socket");
/* setsockopt: Handy debugging trick that lets
* us rerun the server immediately after we kill it;
* otherwise we have to wait about 20 secs.
* Eliminates "ERROR on binding: Address already in use" error.
*/
optval = 1;
setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int));
/*
* build the server’s Internet address
*/
bzero((char *) &serveraddr, sizeof(serveraddr));
/* this is an Internet address */
serveraddr.sin_family = AF_INET;
/* let the system figure out our IP address */
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
/* this is the port we will listen on */
serveraddr.sin_port = htons((unsigned short)portno);
/*
* bind: associate the parent socket with a port
*/
if (bind(parentfd, (struct sockaddr *) &serveraddr,
sizeof(serveraddr)) < 0)
error(“ERROR on binding”);
/*
* listen: make this socket ready to accept connection requests
*/
if (listen(parentfd, 5) < 0) /* allow 5 requests to queue up */
error(“ERROR on listen”);
/*
* main loop: wait for a connection request, echo input line,
* then close connection.
*/
clientlen = sizeof(clientaddr);
while (1) {
/*
* accept: wait for a connection request
*/
Client Flow
First, a socket is created with socket.
Second, build the server’s Internet address. Next, create a connection with the server with connect. Finally, send the message line to the server, and print server’s reply using (write, and read).
Code
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define BUFSIZE 1024
/*
* error - wrapper for perror
*/
void error(char *msg) {
perror(msg);
exit(0);
}
struct sockaddr_in prepare_sock_addr(int portno, char *hostname) {
struct sockaddr_in serveraddr;
struct hostent *server;
/* gethostbyname: get the server’s DNS entry */
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr,”ERROR, no such host as %s\n”, hostname);
exit(0);
}
/* build the server’s Internet address */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
serveraddr.sin_port = htons(portno);
return serveraddr;
}
void cli_send_request(struct sockaddr_in serveraddr) {
/* socket: create the socket */
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error(“ERROR opening socket”);
/* connect: create a connection with the server */
if (connect(sockfd, &serveraddr, sizeof(serveraddr)) < 0)
error(“ERROR connecting”);
/* get message line from the user */
char buf[BUFSIZE];
printf("Please enter msg: “);
bzero(buf, BUFSIZE);
fgets(buf, BUFSIZE, stdin);
/* send the message line to the server */
int n = write(sockfd, buf, strlen(buf));
if (n < 0)
error(“ERROR writing to socket”);
/* print the server’s reply */
bzero(buf, BUFSIZE);
n = read(sockfd, buf, BUFSIZE);
if (n < 0)
error(“ERROR reading from socket”);
printf("Echo from server: %s", buf);
close(sockfd);
}
void client_special_request(struct sockaddr_in serveraddr) {
/* socket: create the socket */
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
/* connect: create a connection with the server */
if (connect(sockfd, &serveraddr, sizeof(serveraddr)) < 0)
error("ERROR connecting");
/* get message line from the user */
char *text = "This is from client!\n";
/* send the message line to the server */
int n = write(sockfd, text, strlen(text));
if (n < 0)
error("ERROR writing to socket");
//printf("Wrote to server: %s", text);
/* print the server's reply */
char buf[BUFSIZE];
bzero(buf, BUFSIZE);
n = read(sockfd, buf, BUFSIZE);
if (n < 0)
error("ERROR reading from socket");
printf("Echo from server: %s", buf);
close(sockfd);
}
// bind and listen code are here
while (1) {
FD_ZERO(&readfds);
FD_SET(parentfd, &readfds);
if (select(parentfd+1, &readfds, 0, 0, 0)< 0) {
error(“Error in select”);
}
if (FD_ISSET(parentfd, &readfds)) {
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
…
Use kqueue for event loop
In Mac, kqueue is similar to epoll.
int kevent(int kq, const struct kevent *changelist,
int nchanges, struct kevent *eventlist,
int nevents, const struct timespec *timeout);
int kq = kqueue();
struct kevent ev_set;
EV_SET(&ev_set, parentfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
struct kevent evList[32];
while (1) {
printf("waiting for client to connect me…\n”);
int nev = kevent(kq, &ev_set, 1, evList, 32, NULL);
for (int i=0; i<nev; i++) {
if (evList[I].ident != parentfd) {
continue;
}
handle_connection(parentfd, serveraddr);
}
}
EV_SET adds parentfd to event set, and monitor when fds is ready for reading. If the result in evList contains the parentfd, we know there is a queued connection, so we start to handle.
A multithreading TCP client
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include “sock_helper.h”
void *fire_request( void *ptr );
int main(void)
{
pthread_t thread1, thread2, thread3;
char *message1 = “Thread 1";
char *message2 = “Thread 2”;
/* Create independent threads each of which will execute function */
pthread_create( &thread1, NULL, fire_request, (void*) message1);
pthread_create( &thread2, NULL, fire_request, (void*) message2);
/* Wait till threads are complete before main continues. Unless we */
/* wait we run the risk of executing an exit which will terminate */
/* the process and all threads before the threads have completed. */
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
return 0;
}
void *fire_request( void *ptr )
{
char *hostname = "localhost";
int portno = 5555;
struct sockaddr_in serveraddr = prepare_sock_addr(portno, hostname);
// Create socket, connect, write msg.
client_special_request(serveraddr);
return NULL;
}