linux socket
when you program with socket under linux, you may find that the steps to handle socket is fixed. so having written the fixed code several times, you may be familiar with it.
the steps for handling socket in server end show below:
1. first of all you should call socket function to create a socket, the prototype of socket function is
int socket(int domain,int type,int protocol)
the first argument sometimes is called address family. when you are handling socket across the internet, you should set this argument AF_INET which is one of the enumeration defined in sys/socket.h header file.
the second argument is about which type of socket you want to create. there are several types, such as SOCK_STREAM and SOCK_DGRAM. when transferring data between client and server, socket with the type SOCK_STREAM transfers the data via byte stream, however, the SOCK_DGRAM socket wraps the transferred data into several packages called datagram.
the third argument is the protocol. we may just set 0 to let the kernel do it for us.
with no error, the socket function returns a valid positive integer which is a file descriptor of the created socket. if something goes wrong, -1 is returned and the global variable errno is set.
by convention, we use the socket with the type SOCK_STREAM for TCP protocol and SOCK_DGRAM for UDP protocol.
having know things above, so we can call
socket(AF_INET,SOCK_STREAM,0)
to create a type of SOCK_STREAM for TCP protocol. and call
socket(AF_INET,SOCK_DGRAM,0)
to create a type of SOCK_DGRAM for UDP protocol.
2. after step 1, we have already created a socket. the next step is binding the created socket to an address. the prototype of this function is
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
as we metioned above, the socket function return a valid file descriptor if everything is OK. so the first argument sockfd is just the socket we created, so is the socket we want to bind.
the second argument is a const pointer to a struct sockaddr which is the address we want bind with. as the argument is qualified with const, so we can't modify it within the bind function. before we can use it, we should initiate it.
the struct sockaddr is a generic address, we may use sockaddr_in instead which is an address for internet purpose. struct sockaddr_in has several attribute, we just list some that may be used during programming, for advanced requirement, you should refer to the mannual under linux.
there are three main attributes which are sin_family, sin_addr and sin_port. we have mentioned the concept of address family. the sin_addr attribute is also a pointer to a struct in_addr_t which is a unsigned integer with 32 bits the length of IPV4 address. what if we don't know the 32 bits of an IPV4 address, how could we initiate the socket with the familiar schema such as 192.168.1.101. there are some functions avaliable for transformation between the familiar dot schema and bit schema, which are inet_pton and inet_ntop. character p here stands for presentation and character n stands for numeric. the presentation schema is always dot schema.
the last argument is sin_port which is a unsigned integer with 16 bit. so the port we can set is 0~65535. one point should be strengthened here is an issue called byte order. i don't want to explain here. all we know is before setting the sin_port we should use function htons or htonl. here character h stands for host and n stands for network.
the last argument is the length of sockaddr_in
as we know above,we can initiate the sockaddr_in with the following:
sockaddr_in socket; socket.sin_family = AF_INET; socket.sin_addr.addr_in = inet_pton("192.168.1.101"); socket.sin_port = htons(1234);
and we use bind function as
bind(sockfd,(struct sockaddr *)socket, sizeof(socket));
3. after binding the socket to a certain address, the next step is calling listen function on the socket to waiting for clients. the prototype of listen function is
int listen(int sockfd, int backlog)
the listen function marks the socket referred to by sockfd as a passive socket, that is, as a socket that will be used to accept incoming connection requests using accept function which i will explain later. the first argument is discussed above, the type of socket's type should be SOCK_STREAM or SOCK_SEQPACKET. the second argument defines the maximun length to which the queue of pending connections for sockfd may grow. if a connection request arrives when is queue is full, the client may recieve an error with indication of ECONNREFUSED.
4. the last step for server end is calling the accept function, this function will block until a client's request arrives. the prototype of this function is
int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen)
the arguments of this function are similar with bind function's, however, the last argument is different which is in this function called value-result argument which is talked about in the last blog named linux value-result argument.
the second argument should be set to the socket address of client. if everything goes well, the function returns a positive integer which stands for a valid file descriptor to the client. so you can use familiar functions such as read, write on the socket to send message to the client end.
all above is the steps for handling socket on the server end.
the next, i will talk steps for handling socket on the client end.
the steps in client end is somehow simple. firt step is to create a sockt with the socket function, after creating, you should call connect function to connect with the server end. the function returns 0 indicating that has connected to server end successfully. so you can use familiar functions such as read, write on the socket to send message to the client end.
after doing this, let's see some a simple example. the server program listens on the port 1234 in the host and the client program in the same host try to connect to the server.
server:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #define BACKLOG 10 int main(int argc, char * argv[]){ struct sockaddr_in server,client; /*declare two socket address for client and server*/ int sockfd,clifd; /*declare two integer for socket file descriptor of client and server*/ int ret_val; int len; const char * sendmsg = "hello world!\n"; if(argc != 2){ fprintf(stderr,"Usage: server <port>!\n"); exit(1); } server.sin_family = AF_INET; server.sin_port = htons(atoi(argv[1])); server.sin_addr.s_addr = htonl(INADDR_ANY); sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0){ fprintf(stderr,"error occurs in creating socket!\n"); exit(1); } ret_val = bind(sockfd,(struct sockaddr*)&server,sizeof(server)); if(ret_val < 0){ fprintf(stderr,"error occurs in binding socket!\n"); exit(1); } ret_val = listen(sockfd, BACKLOG); if(ret_val < 0){ fprintf(stderr,"error occurs in listening!\n"); exit(1); } len = sizeof(client); exit(1); } ret_val = listen(sockfd, BACKLOG); if(ret_val < 0){ fprintf(stderr,"error occurs in listening!\n"); exit(1); } len = sizeof(client); clifd = accept(sockfd,(struct sockaddr*)&client,&len); if(clifd < 0){ fprintf(stderr,"error occurs in accepting!\n"); exit(1); } ret_val = write(clifd,sendmsg,strlen(sendmsg)+1); if(ret_val < 0){ fprintf(stderr,"error occurs in writing to client!\n"); exit(1); } close(clifd); }
client:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #define BUF_SIZE 128 int main(int argc, char* argv[]){ struct sockaddr_in server; int sockfd; int ret_val; char buf[BUF_SIZE]; if(argc != 3){ fprintf(stderr,"Usage: client <server address> <port>!\n"); exit(1); } server.sin_family = AF_INET; inet_pton(AF_INET,argv[1],&server.sin_addr); server.sin_port = htons(atoi(argv[2])); sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd < 0){ fprintf(stderr,"error occurs in creating socket!\n"); exit(1); } ret_val = connect(sockfd,(struct sockaddr*)&server,sizeof(server)); if(ret_val < 0){ fprintf(stderr,"error occurs in conneting to server!\n"); exit(1); } bzero(buf,sizeof(buf)); read(sockfd,buf,BUF_SIZE); fprintf(stdout,buf,strlen(buf)); close(sockfd); }