Download presentation
Presentation is loading. Please wait.
1
Socket Programming in C
Professor: Dr. Shu-Ching Chen TA: Samira Pouyanfar, Hector Cen, Tianyi Wang
2
Networking terms Packet - the smallest unit that can be transferred “through” the network by itself Protocol - a set of rules and conventions for communication between network devices A collection of protocol layers is referred to as a “protocol suite”, “protocol family” or “protocol col family” or “protocol stack”. TCP/IP is one such protocol suite.
3
What is socket? An interface between application processes
An Application Programming Interface (API) used for Inter-Process Communications (IPC) Sockets bound to some IP and port number The socket library provides various system calls Examples: socket(), bind(), listen(), connect(), accept(),send(), recv(), close() – The application process can send/receive messages to/from another application process (local or remote)via a socket An interface through which processes can send /receive information Socket: An interface between an application process and transport layer A well defined method of connecting two processes, defined method of connecting two processes, locally or across a network
4
Socket Description There are mechanisms based on the socket interface. Sockets allow the Linux kernel to send notifications to a user space application. This is in contrast to the file system mechanisms, where the kernel can alter a file, but the user program does not know of any change until it choses to access that file. The socket based mechanisms allow the applications to listen on a socket, and the kernel can send them messages at any time. This leads to a communication mechanism in which user space and kernel space are equal partners. Application Layer The Application Layer is where applications requiring network communications live. Examples of these applications include clients and web browsers. These applications use the Transport Layer to send requests to connect to remote hosts. Transport Layer The Transportation Layer establishes the connection between applications running on different hosts. It uses TCP for reliable connections and UDP for fast connections. It keeps track of the processes running in the applications above it by assigning port numbers to them and uses the Network layer to access the TCP/IP network. Network Layer The Network layer is responsible for creating the packets that move across the network. It uses IP addresses to identify the packet’s source and destination. Data Link Layer The Data Link layer is responsible for creating the frames that move across the network. These frames encapsulate the packets and use MAC addresses to identify the source and destination. Physical Layer The Physical layer encodes and decodes the bits found in a frame and includes the transceiver that drives and receives the signals on the network. Let’s see how they work together to send and receive data across a TCP/IP network. This is a simplified view of how the network layers work together to generate frames. Higher layers pass information to lower layers. Each layer adds information called a header to the data being passed to it. This header contains information the layer needs to perform its job. We will start at the Application layer. The Application layer generates a message. In this case, the specific application is a web browser requesting a webpage download. This message is then sent to the Transport layer. The Transport layer adds the TCP or UDP header which includes the source and destination port addresses. Additional information like the packet sequence number used for TCP will also be added to the header. The data generated by the transport layer is referred to as a Segment if TCP is used, and is referred to as a Datagram if UDP is used. This segment is then sent to the Network layer. The Network layer adds a header including the source and destination IP address to generate a packet. This packet is then sent to the Data Link layer. The Data Link layer adds a header containing the MAC address information to create a frame. The frame is then sent it to the Physical layer to transmit the bits.
5
Client-server paradigm
initiates contact with server (“speaks first”) typically requests service from server, For Web, client is implemented in browser; Server: provides requested service to client typically requests service from server, e.g., Web server sends requested Web page. Most inter-process communication uses the client server model. These terms refer to the two processes which will be communicating with each other. One of the two processes, the client, connects to the other process, the server, typically to make a request for information. A good analogy is a person who makes a phone call to another person. Notice that the client needs to know of the existence of and the address of the server, but the server does not need to know the address of (or even the existence of) the client prior to the connection being established. Notice also that once a connection is established, both sides can send and receive information. The system calls for establishing a connection are somewhat different for the client and the server, but both involve the basic construct of a socket. A socket is one end of an inter-process communication channel. The two processes each establish their own socket.
6
Types of socket Two types of internet Sockets
Stream sockets SOCK_STREAM Connection oriented Rely on TCP to provide reliable two-way connected communication Datagram sockets SOCK_DGRAM Rely on UDP Connection is unreliable There are two widely used socket types, stream sockets, and datagram sockets. Stream sockets treat communications as a continuous stream of characters, while datagram sockets have to read entire messages at once. Each uses its own communications protocol. Stream sockets use TCP (Transmission Control Protocol), which is a reliable, stream oriented protocol, and datagram sockets use UDP (Unix Datagram Protocol), which is unreliable and message oriented. UDP is only used for streaming audio and video. UDP is only concerned with speed.
7
User Datagram protocol(UDP)
Application Interface to IP – Packet Oriented Establishes a “port”, which allows IP to distinguish among processes running on the same host Features resemble IP semantics: Connectionless Unreliable
8
User Datagram protocol(UDP)
Datagram sockets- connectionless socket (UDP) Algorithm for UDP client: -Find the IP address and port number of server -Create a UDP socket -Send-receive data with server using the socket -close the connection Algorithm for UDP server: -Bind the server socket to server IP and port number -Accept a new connection from client -Send-receive data with client using the client socket -close the connection with client UDP user datagram protocol A datagram socket provides a symmetric data exchange interface without requiring connection establishment. Each message carries the destination address. sendto(s, buf, buflen, flags, (struct sockaddr *) &to, tolen); The s, buf, buflen, and flags parameters are the same as in connection-oriented sockets. The to and tolen values indicate the address of the intended recipient of the message. A locally detected error condition, such as an unreachable network, causes a return of -1 and errno to be set to the error number. recvfrom(s, buf, buflen, flags, (struct sockaddr *) &from, &fromlen); To receive messages on a datagram socket, recvfrom(3SOCKET) is used. Before the call, fromlen is set to the size of the from buffer. On return, fromlen is set to the size of the address from which the datagram was received.
9
Transmission Control Protocol(TCP)
Connection-oriented Stream Data Transfer Reliable Flow-Control Full-Duplex Suited for critical data transfer applications Full-duple: It means exchanging data(sending and receiving) between two entities at the same time
10
Transmission Control Protocol(TCP)
Stream sockets – connection-oriented (TCP) Server Client socket() socket() bind() listen() connect() Connection–based sockets communicate client-server: the server waits for a connection from the client Three-way handshake Algorithm for TCP client: -Find the IP address and port number of server -Create a TCP socket -Connect the socket to server -Send-receive data with server using the socket -close the connection Algorithm for TCP server: -Bind the server socket to server IP and port number -Accept a new connection from client -Send-receive data with client using the client socket -close the connection with client accept() send/recv() send/recv() close() close()
11
Primary Socket Calls (1)
Server Client socket() socket() bind() listen() connect() accept() send/recv() send/recv() close() close()
12
Primary Socket Calls (1)
socket() : Returns a file descriptor(socket ID) if successful, -1 otherwise. Arguments: Domain: set to AF_INET Type: SOCK_STREAM SOCK_DGRAM SOCK_SEQPACKET Protocol: If it is set as zero, then socket will choose the correct protocol based on type. int socket(int domain, int type, int protocol); The domain argument specifies the address family used in the communications domain. Protocol Family: PF_INET or PF_UNI The type argument can be: SOCK STREAM: Establishes a virtual circuit for stream (TCP) SOCK DGRAM: Establishes a datagram for communication (UDP) SOCK SEQPACKET: Establishes a reliable, connection based, two way communication with maximum message size. (This is not available on most machines.) protocol is usually zero, so that type defines the connection within domain. The call returns a integer identifier called a handle
13
Example 1 #include<stdio.h> 2 #include<sys/socket.h> 3 4 int main(int argc , char *argv[ ]){ 5 int socket_desc; 6 socket_desc = socket(AF_INET , SOCK_STREAM , 0); 7 8 if (socket_desc == -1) 9 { 10 printf("Could not create socket"); 11 } return 0; 14 } The script above creates a socket with the following properties: Address Family: AF_INET (this is IP version 4) Type: SOCK_STREAM (this means connection oriented TCP protocol) Protocol: 0 (or IPPROTO_IP This is IP protocol) Remember to include the sys/socket.h library
14
Primary Socket Calls (2)
Server Client socket() socket() bind() listen() connect() accept() send/recv() send/recv() close() close()
15
Primary Socket Calls (2)
bind() : Associate a socket id with an address to which other processes can connect. On success, zero is returned. On error, -1 is returned, and errno is set appropriately. Arguments Sockfd: It is the socket id My_addr: a pointer to the address family dependent address structure Addrlen: It is the size of *my_addr int bind(int sockfd, struct sockaddr *my_addr, int addrlen); sockfd is the socket descriptor returned by socket() – my_addr is pointer to struct sockaddr that contains information about your IP address and port – addrlen is set to sizeof(struct sockaddr) – returns -1 on error – my_addr.sin_port = 0; //choose an unused port at random – my_addr.sin_addr.s_addr = INADDR_ANY; //use my IP adr The actual structure passed for the my_addr argument will depend on the address family. The sockaddr structure is defined as something like: struct sockaddr { sa_family_t sa_family; char sa_data[14] } The only purpose of this structure is to cast the structure pointer passed in my_addr in order to avoid compiler warnings.
16
Example 1 #include <sys/socket.h> 2 #include <sys/un.h> 3 #include <stdlib.h> 4 #include <stdlio.h> 5 #define MY_SOCK_PATH "/somepath" 6 int main(int argc, char *argv[]) { 7 int sfd; 8 struct sockaddr_un addr; 9 sfd = socket(AF_UNIX, SOCK_STREAM, 0); 10 if (sfd == -1) { 11 perror("socket"); 12 exit(EXIT_FAILURE); } 13 memset(&addr, 0, sizeof(struct sockaddr_un)); 14 /* Clear structure */ 15 addr.sun_family = AF_UNIX; 16 strncpy(addr.sun_path, MY_SOCK_PATH, sizeof(addr.sun_path) - 1); 17 if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) { 18 perror("bind"); exit(EXIT_FAILURE); } } This script shows an example of bind() function. In line 8: declares structure sockaddr_un. Sockaddr_un is used due to the type of socket descriptor domain we use, which is AF_UNIX here. In line 9: sfd has been assigned the value of socket id from socket() function. Line 10~11: test the returned value of socket() function. Line 13: memset() function is used to fill a block of memory with a particular value, here we use it to clear/initialize all the struct variables to zero, which is a good practice: void *memset(void *ptr, int x, size_t n); // ptr ==> Starting address of memory to be filled // x ==> Value to be filled // n ==> Number of bytes to be filled starting from ptr to be filled Line 16: copy “sizeof(addr.sun_path)” length of string from “MY_SOCK_PATH” to “addr.sun_path”. Line 17: bind and test the returned value. Output error message and exit if failed (return -1)
17
Primary Socket Calls (3)
Server Client socket() socket() bind() listen() connect() accept() send/recv() send/recv() close() close()
18
Primary Socket Calls (3)
listen() : Return 0 on success, or –1 if failure Arguments Sockfd: It is socket id created by socket() Backlog : It is used to constraint the number of connection int listen(int sockfd, int backlog); listen for connections on a socket SYNOPSIS Where size is the number of pending connection requests allowed (typically limited by Unix kernels to 5). To accept connections, a socket is first created with socket(2), a willingness to accept incoming connections and a queue limit for incoming connections are specified with listen(), and then the connections are accepted with accept(). The listen() call applies only to sockets of type SOCK_STREAM or SOCK_SEQPACKET. The backlog parameter defines the maximum length the queue of pending connections may grow to. If a connection request arrives with the queue full the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission, the request may be ignored so that retries succeed.
19
Example This script shows an example of listen() function.
1 #include <sys/socket.h> 2 #include <sys/un.h> 3 #include <stdlib.h> 4 #include <stdlio.h> 5 #define MY_SOCK_PATH "/somepath" 6 #define LISTEN_BACKLOG 50 7 int main(int argc, char *argv[]) { 8 int sfd; 9 struct sockaddr_un addr; 10 sfd = socket(AF_UNIX, SOCK_STREAM, 0); 11 if (sfd == -1) { 12 perror("socket"); 13 exit(EXIT_FAILURE); } 14 memset(&addr, 0, sizeof(struct sockaddr_un)); 15 /* Clear structure */ 16 addr.sun_family = AF_UNIX; 17 strncpy(addr.sun_path, MY_SOCK_PATH, sizeof(addr.sun_path) - 1); 18 if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) { 19. perror("bind"); exit(EXIT_FAILURE); } 20 if (listen(sfd, LISTEN_BACKLOG) == -1){ 21 perror(”listen"); exit(EXIT_FAILURE);} } This script shows an example of listen() function. In line 20 test if listen() is successful. Return error message and exit if returned “-1”
20
Primary Socket Calls (4)
Server Client socket() socket() bind() listen() connect() accept() send/recv() send/recv() close() close()
21
Primary Socket Calls (4)
connect() : connect to a remote host Arguments Sockfd: It is the socket descriptor returned by socket() serv_addr : It is a pointer to to struct sockaddr that contains information on destination IP address and port Addrlen: It is set to sizeof(struct sockaddr) int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); Specifies the destination to form a connection with (addrPtr), and returns a 0 if successful, -1 otherwise. The connect() system call connects the socket referred to by the file descriptor sockfd to the address specified by serv_addr. The addrlen argument specifies the size of serv_addr. The format of the address in serv_addr is determined by the address space of the socket sockfd; see socket(2) for further details. If the socket sockfd is of type SOCK_DGRAM then serv_addr is the address to which datagrams are sent by default, and the only address from which datagrams are received. If the socket is of type SOCK_STREAM or SOCK_SEQPACKET, this call attempts to make a connection to the socket that is bound to the address specified by serv_addr. Generally, connection-based protocol sockets may successfully connect() only once; connectionless protocol sockets may use connect() multiple times to change their association. Connectionless sockets may dissolve the association by connecting to an address with the sa_family member of sockaddr set to AF_UNSPEC.
22
Primary Socket Calls (5)
Server Client socket() socket() bind() listen() connect() accept() send/recv() send/recv() close() close()
23
Primary Socket Calls (5)
accept() : gets the pending connection on the port you are listen() on. Returns a new file descriptor referring to that socket, -1 if failed. Arguments Sockfd: It is the same socket id used by listen() Addr: It is a pointer to a local struct sockaddr which stores the information about incoming connection Addrlen: It is set to sizeof(struct sockaddr_in) int accept(int sockfd, struct sockaddr *addr, int *addrlen); The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection request on the queue of pending connections, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket sockfd is unaffected by this call. The argument sockfd is a socket that has been created with socket(), bound to a local address with bind(), and is listening for connections after a listen(). The argument addr is a pointer to a sockaddr structure. This structure is filled in with the address of the peer socket, as known to the communications layer. The exact format of the address returnedaddr is determined by the socket’s address family (see socket() and the respective protocol man pages). The addrlen argument is a value-result argument: it should initially contain the size of the structure pointed to by addr; on return it will contain the actual length (in bytes) of the address returned. When addr is NULL nothing is filled in. If no pending connections are present on the queue, and the socket is not marked as non-blocking, accept() blocks the caller until a connection is present. If the socket is marked non-blocking and no pending connections are present on the queue, accept() fails with the error EAGAIN.
24
Example This script shows an example of accept() function.
1 #include <sys/socket.h> 2 #include <sys/un.h> 3 #include <stdlib.h> 4 #include <stdlio.h> 5 #define MY_SOCK_PATH "/somepath" 6 #define LISTEN_BACKLOG 50 7 int main(int argc, char *argv[]) { 8 int sfd, cfd; 9 struct sockaddr_un addr, peer_addr; 10 sfd = socket(AF_UNIX, SOCK_STREAM, 0); 11 if (sfd == -1) { 12 perror("socket"); 13 exit(EXIT_FAILURE); } 14 memset(&addr, 0, sizeof(struct sockaddr_un)); 15 /* Clear structure */ 16 addr.sun_family = AF_UNIX; 17 strncpy(addr.sun_path, MY_SOCK_PATH, sizeof(addr.sun_path) - 1); 18 if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) { 19. perror("bind"); exit(EXIT_FAILURE); } 20 if (listen(sfd, LISTEN_BACKLOG) == -1){ 21 perror(”listen"); exit(EXIT_FAILURE);} This script shows an example of accept() function.
25
Example Cont. 22 socklen_t peer_addr_size; 23 /* Now we can accept incoming connections one at a time using accept() */ 24 peer_addr_size = sizeof(struct sockaddr_un); 25 cfd = accept(sfd, (struct sockaddr *) &peer_addr, &peer_addr_size); 26 if (cfd == -1) 27 handle_error("accept"); 28 /* Code to deal with incoming connection(s)... */ 29 /* When no longer required, the socket pathname, MY_SOCK_PATH should be deleted using unlink() or remove() */ } This script shows an example of accept() function. Line 22: declare a socklen_t variable, which is an unsiged opaque integral type of length of at least 32-bits used for socket lib. By declaring specific types for these fields, it decouples them from a particular representation like unsigned int. Different architectures can be free to define different sizes for these fields, and code that uses these specific types doesn't need to worry about how big an int is on a given machine. Line 24: assign the length of sockaddr_un to peer_addr_size Line 25: use accept() function to get a new socket descriptor assigned to variable “cfd” Line 26: test if accept() function call succeed.
26
Primary Socket Calls (6)
Server Client socket() socket() bind() listen() connect() accept() send/recv() send/recv() close() close()
27
Primary Socket Calls (6)
send() : Send a message. Returns the number of bytes sent or -1 if failure. Arguments Sockfd: It is the same socket id used by socket() or accept() msg: It is the pointer to the data you want to send Len: data length is sizeof(msg) Flags : It is set to be zero int send(int sockfd, const void *msg, int len, int flags); flag is either: 0: default MSG OOB: Out-of-band high priority communication The send() call may be used only when the socket is in a connected state (so that the intended recipient is known). The message is found in msg and has length len. The only difference between send() and write() is the presence of flags. With zero flags parameter, send() is equivalent to write(). When the message does not fit into the send buffer of the socket, send() normally blocks, unless the socket has been placed in non-blocking I/O mode. In non-blocking mode it would return EAGAIN in this case.
28
example 1 #include<stdio.h> 2 #include<string.h> //strlen 3 #include<sys/socket.h> 4 #include<arpa/inet.h> //inet_addr 5 int main(int argc , char *argv[]) 6 { 7 int socket_desc; 8 struct sockaddr_in server; 9 char *message; //Create socket 12 socket_desc = socket(AF_INET , SOCK_STREAM , 0); 13 if (socket_desc == -1) 14 { 15 printf("Could not create socket"); 16 } 17 server.sin_addr.s_addr = inet_addr(" "); 18 server.sin_family = AF_INET; 19 server.sin_port = htons( 80 ); This script is a example to demonstrate the use of send() function. In line 34: char pointer “message” is sent through the send() function to “socket_desc” socket descriptor created by the socket() function.
29
Example Cont. 20 //Connect to remote server 21 if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) 22 { 23 puts("connect error"); 24 return 1; 25 } puts("Connected\n"); //Send some data 30 message = "GET / HTTP/1.1\r\n\r\n"; 31 if( send(socket_desc , message , strlen(message) , 0) < 0) 32 { 33 puts("Send failed"); 34 return 1; 35 } 36 puts("Data Send\n"); return 0; 39 }
30
Primary Socket Calls (7)
Server Client socket() socket() bind() listen() connect() accept() send/recv() send/recv() close() close()
31
Primary Socket Calls (7)
recv() : Receive up to len bytes in buf. Returns the number of bytes received or -1 on failure. Arguments Sockfd: It is the socket descriptor to read from buf: It is the buffer to read the information info Len: It is the maximum length of the buffer Flags : It is set to be zero int recv(int sockfd, void *buf, int len, unsigned int flags); returns the number of bytes actually read into the buffer or -1 on error returns 0, the remote side has closed connection on you flags can be either 0: default MSG OOB: out-of-bound message MSG PEEK: look at message without removing The recv() call is normally used only on a connected socket (after the use of connect())and is identical to recvfrom() with a NULL from parameter.
32
example 1 #include<stdio.h> 2 #include<string.h> //strlen 3 #include<sys/socket.h> 4 #include<arpa/inet.h> //inet_addr 5 6 int main(int argc , char *argv[]){ 7 int socket_desc; 8 struct sockaddr_in server; 9 char *message , server_reply[2000]; 10 //Create socket 11 socket_desc = socket(AF_INET , SOCK_STREAM , 0); 12 if (socket_desc == -1){ 13 printf("Could not create socket"); 14 } 15 server.sin_addr.s_addr = inet_addr(" "); 16 server.sin_family = AF_INET; 17 server.sin_port = htons( 80 ); 18 //Code for connecting to remote server …// 19 message = "GET / HTTP/1.1\r\n\r\n"; 20 if( send(socket_desc , message , strlen(message) , 0) < 0){ 21 puts("Send failed"); 22 return 1;} 23 puts("Data Send\n"); 24 25 //Receive a reply from the server 26 if( recv(socket_desc, server_reply , 2000 , 0) < 0){ 27 puts("recv failed");} 28 puts("Reply received\n"); 29 puts(server_reply); 30 return 0; 31 } This script is a example to demonstrate the use of recv() function Note: In Line 26: the message received from the server will be stored in “server_reply”. When receiving data on a socket , we are basically reading the data on the socket. This is similar to reading data from a file. So we can also use the read function to read data on a socket. For example : read(socket_desc, server_reply , 2000);
33
Primary Socket Calls (8)
shutdown() : disable sending or receiving based on the value how. Arguments Sockfd How Set it to 0 will disable receiving Set it to 1 will disable sending Set it to 2 will disable both sending and receiving int shutdown(int sockfd, int how); On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
34
Primary Socket Calls (9)
Server Client socket() socket() bind() listen() connect() accept() send/recv() send/recv() close() close()
35
Primary Socket Calls (9)
Close() : Close connection corresponding to the socket descriptor and frees the socket descriptor. int close(int sockfd)
36
Byte Ordering Two types of “Byte ordering” Conversion:
Network Byte order: High-order byte of the number is stored in memory at the lowest address (Big-Endian) Host Byte Order: Low-order byte of the number is stored in memory at the lowest address (Little-Endian) Network stack (TCP/IP) expects Network Byte Order Conversion: htons() – Host to Network Short htonl() – Host to Network Long ntohs() – Network to Host Short ntohl() – Network to Host Long Unfortunately, not all computers store the bytes that comprise a multibyte value in the same order. Consider a 16-bit internet that is made up of 2 bytes. There are two ways to store this value. Little Endian − In this scheme, low-order byte is stored on the starting address (A) and high-order byte is stored on the next address (A + 1). Big Endian − In this scheme, high-order byte is stored on the starting address (A) and low-order byte is stored on the next address (A + 1). To allow machines with different byte order conventions communicate with each other, the Internet protocols specify a canonical byte order convention for data transmitted over the network. This is known as Network Byte Order. While establishing an Internet socket connection, you must make sure that the data in the sin_port and sin_addr members of the sockaddr_in structure are represented in Network Byte Order.
37
How to make a client The steps involved in establishing a socket on the client side are as follows − Create a socket with the socket() system call. Connect the socket to the address of the server using the connect()system call. Send and receive data. There are a number of ways to do this, but the simplest way is to use the read()/recv() and write()/send() system calls. The system calls for establishing a connection are somewhat different for the client and the server, but both involve the basic construct of a socket. Both the processes establish their own sockets.
38
How to make a Server The steps involved in establishing a socket on the server side are as follows − Create a socket with the socket() system call. Bind the socket to an address using the bind() system call. For a server socket on the Internet, an address consists of a port number on the host machine. Listen for connections with the listen() system call. Accept a connection with the accept() system call. This call typically blocks the connection until a client connects with the server. Send and receive data using the read()/recv() and write()/send() system calls.
39
portno stores the port number on which the server accepts connections
example – server (1) This header file contains definitions of a number of data types used in system calls. These types are used in the next two include files. The header file socket.h includes a number of definitions of structures needed for sockets. The header file in.h contains constants and structures needed for internet domain addresses. This header file contains declarations used in most input and output and is typically included in all C programs. The header file socket.h includes a number of definitions of structures needed for sockets. 1 #include <stdio.h> 2 #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> 7 void error(const char *msg) { perror(msg); exit(1); } 13 14 int main(int argc, char *argv[ ] ){ int sockfd, newsockfd, portno, n; socklen_t chilen; char buffer[256]; struct sockaddr_in serv_addr, cli_addr; if (argc < 2) { fprintf(stderr, “ERROR, no port provided\n”); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error(“ERROR opening socket”); bzero((char *) &serv_addr, sizeof(serv_addr)); portno = atoi(argv[1]); portno stores the port number on which the server accepts connections Sockfd and newsockfd are file descriptors. They store the values returned by the socket system call and the accept system call This function is called when a system call fails. It displays a message about the error on stderr and then aborts the program Definition: struct sockaddr_in{ short sin_family; /* must be AF_INET */ u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; /* Not used, must be zero */ }; A sockaddr_in is a structure containing an internet address. This structure is defined in netinet/in.h An in_addr structure, defined in the same header file, contains only one filed, a unsigned long called s_addr. The variable serv_addr will contain the address of the server, and cli_addr will contain the address of the client which connects to the server clilen stores the size of the address of the client. This is needed for the accept system call n is the return value for the read() and write() calls; i.e. it contains the number of characters read or written The server reads characters from the socket connection into this buffer The socket() system call creates a new socket and returns an entry into the file descriptor table. This value is used for all subsequent references to this socket. The user needs to pass in the port number on which the server will accept connections as an argument. This code displays an error message if the user fails to do this. read() is equivalent to recv() with a flags parameter of 0. Similarly, write() is equivalent to send() with flags == 0. Other values for the flags parameter change the behaviour of recv(). - sockfd and newsockfd are file descriptors. These two variables store the values returned by the socket system call and the accept system call. Void bzero(void *s, size_t n) function will place n zero-valued bytes in the area pointed to by s. Line by line explanation: uint16_t htons(uint16_t hostshort) function converts the unsigned short integer hostshort from host byte order to network bbyte order. INADDR_ANY is used when you don't need to bind a socket to a specific IP. When you use this value as the address when calling bind() , the socket accepts connections to all the IPs of the machine. This header file contains definitions of a number of data types used in system calls. These types are used in the next two include files. #include <sys/types.h>: The header file socket.h includes a number of definitions of structures needed for sockets. #include <sys/socket.h>: The header file in.h contains constants and structures needed for internet domain addresses. #include <netinet/in.h>: -portno stores the port number on which the server accepts connections. This function is called when a system call fails. It displays a message about the error on stderr and then aborts the program. void error(char *msg): -struct sockaddr_in serv_addr, cli_addr; A sockaddr_in is a structure containing an internet address. This structure is defined in netinet/in.h. -char buffer[256]; The server reads characters from the socket connection into this buffer. -clilen stores the size of the address of the client. This is needed for the accept system call. struct sockaddr_in { short sin_family; /* must be AF_INET */ u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; /* Not used, must be zero */ }; An in_addr structure, defined in the same header file, contains only one field, a unsigned long called s_addr. The variable serv_addr will contain the address of the server, and cli_addr will contain the address of the client which connects to the server. Here is the definition: if (argc < 2) { fprintf(stderr,"ERROR, no port provided "); exit(1); } The user needs to pass in the port number on which the server will accept connections as an argument. This code displays an error message if the user fails to do this. serv_addr.sin_family = AF_INET; The variable serv_addr is a structure of type struct sockaddr_in. This structure has four fields. The first field is short sin_family, which contains a code for the address family. It should always be set to the symbolic constant AF_INET. serv_addr.sin_port = htons(portno); The second field of serv_addr is unsigned short sin_port, which contain the port number. However, instead of simply copying the port number to this field, it is necessary to convert this to network byte order using the function htons() which converts a port number in host byte order to a port number in network byte order. serv_addr.sin_addr.s_addr = INADDR_ANY; The third field of sockaddr_in is a structure of type struct in_addr which contains only a single field unsigned long s_addr. This field contains the IP address of the host. For server code, this will always be the IP address of the machine on which the server is running, and there is a symbolic constant INADDR_ANY which gets this address. if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error("ERROR on binding"); The bind() system call binds a socket to an address, in this case the address of the current host and port number on which the server will run. It takes three arguments, the socket file descriptor, the address to which is bound, and the size of the address to which it is bound. The second argument is a pointer to a structure of type sockaddr, but what is passed in is a structure of type sockaddr_in, and so this must be cast to the correct type. This can fail for a number of reasons, the most obvious being that this socket is already in use on this machine. listen(sockfd,5); The listen system call allows the process to listen on the socket for connections. The first argument is the socket file descriptor, and the second is the size of the backlog queue, i.e., the number of connections that can be waiting while the process is handling a particular connection. This should be set to 5, the maximum size permitted by most systems. If the first argument is a valid socket, this call cannot fail, and so the code doesn't check for errors. clilen = sizeof(cli_addr); newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) error("ERROR on accept"); The accept() system call causes the process to block until a client connects to the server. Thus, it wakes up the process when a connection from a client has been successfully established. It returns a new file descriptor, and all communication on this connection should be done using the new file descriptor. The second argument is a reference pointer to the address of the client on the other end of the connection, and the third argument is the size of this structure. bzero(buffer,256); n = read(newsockfd,buffer,255); if (n < 0) error("ERROR reading from socket"); printf("Here is the message: %s ",buffer); Note that we would only get to this point after a client has successfully connected to our server. This code initializes the buffer using the bzero()function, and then reads from the socket. Note that the read call uses the new file descriptor, the one returned by accept(), not the original file descriptor returned by socket(). Note also that the read() will block until there is something for it to read in the socket, i.e. after the client has executed a write().It will read either the total number of characters in the socket or 255, whichever is less, and return the number of characters read. n = write(newsockfd,"I got your message",18); if (n < 0) error("ERROR writing to socket"); Once a connection has been established, both ends can both read and write to the connection. Naturally, everything written by the client will be read by the server, and everything written by the server will be read by the client. This code simply writes a short message to the client. The last argument of write is the size of the message. return 0; } This terminates main and thus the program. Since main was declared to be of type int as specified by the ascii standard, some compilers complain if it does not return anything. The port number on which the server will listen for connections is passed in as an argument, and this statement uses the atoi() function to convert this from a string of digits to an integer The function bzero() sets all values in a buffer to zero. Thus, this line initializes serv_addr to zeros.
40
example – server (2) The accept() system call causes the process to block until a client connects to the server. Thus, it wakes up the process when a connection from a client has been successfully established. It returns a new file descriptor, and all communication on this connection should be done using the new file descriptor. The second argument is a reference pointer to the address of the client on the other end of the connection, and the third argument is the size of this structure. The second field of serv_addr is unsigned short sin_port, which contain the port number. However, instead of simply copying the port number to this field, it is necessary to convert this to network byte order using the function htons() which converts a port number in host byte order to a port number in network byte order. The bind() system call binds a socket to an address, in this case the address of the current host and port number on which the server will run. The third field of sockaddr_in is a structure of type struct in_addr which contains only a single field unsigned long s_addr. This field contains the IP address of the host. For server code, this will always be the IP address of the machine on which the server is running, and there is a symbolic constant INADDR_ANY which gets this address. The variable serv_addr is a structure of type struct sockaddr_in. This structure has four fields. The first field is short sin_family, which contains a code for the address family. It should always be set to the symbolic constant AF_INET. The listen system call allows the process to listen on the socket for connections. The first argument is the socket file descriptor, and the second is the size of the backlog queue, i.e., the number of connections that can be waiting while the process is handling a particular connection. This should be set to 5, the maximum size permitted by most systems. If the first argument is a valid socket, this call cannot fail, and so the code doesn't check for errors. serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(portno); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error(“ERROR on binding”); listen(sockfd,5); clilen = sizeof(cli_addr); newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) error(“ERROR on accept”); bzero(buffer, 256); n = read(newsockfd, buffer, 255); if (n < 0) error(“ERROR reading from socket”); printf(“Here is the message: %s\n”, buffer); n = write(newsockfd, “I got your message”, 18); if (n < 0) error (“ERROR writing to socket”); close(newsockfd); close(sockfd); return 0; } Once a connection has been established, both ends can both read and write to the connection. Naturally, everything written by the client will be read by the server, and everything written by the server will be read by the client. This code simply writes a short message to the client. The last argument of write is the size of the message. Note also that the read() will block until there is something for it to read in the socket, i.e. after the client has executed a write(). It will read either the total number of characters in the socket or 255, whichever is less, and return the number of characters read. Note that we would only get to this point after a client has successfully connected to our server. This code initializes the buffer using the bzero() function, and then reads from the socket. Note that the read call uses the new file descriptor, the one returned by accept(), not the original file descriptor returned by socket(). bzero function will be used to set all the socket structures with null values void bzero(void *s, int nbyte); This function does not return anything. Parameters: s: specifies string which has to be filled with null bytes.This will be a point to socket structure variable nbyte: specifies the number of bytes to be filled with null values. This will be the size of the socket structure.
41
All of this code is the same as that in the server.
Example – Client (1) 1 #include <stdio.h> 2 #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> void error(const char *msg) { perror(msg); exit(0); } 13 14 int main(int argc, char *argv[ ] ){ int sockfd, portno, n; struct sockaddr_in serv_addr; struct hostent *server; char buffer[256]; if (argc < 3) { fprintf(stderr, “usage %s hostname port\n”, argv[0]); portno = atoi(argv[2]); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error(“ERROR opening socket”); The header files are the same as for the server with one addition. The file netdb.h defines the structure hostent, which will be used below. It defines a host computer on the Internet. The members of this structure are: h_name: Official name of the host. h_aliases: A zero terminated array of alternate names for the host. h_addrtype: The type of address being returned; currently always AF_INET. h_length: The length, in bytes, of the address. h_addr_list: A pointer to a list of network addresses for the named host. Host addresses are returned in network byte order. Note that h_addr is an alias for the first address in the array of network addresses. The error() function is identical to that in the server, as are the variables sockfd, portno, and n. The variable serv_addr will contain the address of the server to which we want to connect. It is of type struct sockaddr_in. The variable server is a pointer to a structure of type hostent. This structure is defined in the header file netdb.h as follows: struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses from name server */ #define h_addr h_addr_list[0] /* address, for backward compatiblity */ }; The header files are the same as for the server with one addition. The file netdb.h defines the structure hostent, which will be used below: Line by line code explanation: void error(char *msg) { perror(msg); exit(0); } int main(int argc, char *argv[]) { int sockfd, portno, n; struct sockaddr_in serv_addr; struct hostent *server; The error() function is identical to that in the server, as are the variables sockfd, portno, and n. The variable serv_addr will contain the address of the server to which we want to connect. It is of type struct sockaddr_in struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses from name server */ #define h_addr h_addr_list[0] /* address, for backward compatiblity */ }; The variable server is a pointer to a structure of type hostent. This structure is defined in the header file netdb.h as follows: It defines a host computer on the Internet. The members of this structure are: h_name: Official name of the host. h_aliases: A zero terminated array of alternate names for the host. h_addrtype: The type of address being returned; currently always AF_INET. h_length: The length, in bytes, of the address. h_addr_list: A pointer to a list of network addresses for the named host. Host addresses are returned in network byte order. Note that h_addr is an alias for the first address in the array of network addresses. char buffer[256]; if (argc < 3) { fprintf(stderr,"usage %s hostname port ", argv[0]); exit(0); } portno = atoi(argv[2]); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); All of this code is the same as that in the server. server = gethostbyname(argv[1]); if (server == NULL) { fprintf(stderr,"ERROR, no such host "); exit(0); } Takes such a name as an argument and returns a pointer to a hostent containing information about that host. The field char *h_addr contains the IP address. If this structure is NULL, the system could not locate a host with this name. struct hostent *gethostbyname(char *name) The variable argv[1] contains the name of a host on the Internet, e.g. cs.rpi.edu. The function: bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); This code sets the fields in serv_addr. Much of it is the same as in the server. However, because the field server->h_addr is a character string, we use the function: void bcopy(char *s1, char *s2, int length) - if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0) error("ERROR connecting"); which copies length bytes from s1 to s The connect function is called by the client to establish a connection to the server. It takes three arguments, the socket file descriptor, the address of the host to which it wants to connect (including the port number), and the size of this address. This function returns 0 on success and -1 if it fails. Notice that the client needs to know the port number of the server, but it does not need to know its own port number. This is typically assigned by the system when connect is called. printf("Please enter the message: "); bzero(buffer,256); fgets(buffer,255,stdin); n = write(sockfd,buffer,strlen(buffer)); if (n < 0) error("ERROR writing to socket"); bzero(buffer,256); n = read(sockfd,buffer,255); if (n < 0) error("ERROR reading from socket"); printf("%s ",buffer); return 0; } The remaining code should be fairly clear. It prompts the user to enter a message, uses fgets to read the message from stdin, writes the message to the socket, reads the reply from the socket, and displays this reply on the screen. All of this code is the same as that in the server.
42
Example – Client (2) server = gethostbyname(argv[1]); if (server == NULL) { fprintf(stderr, “ERROR, no such host\n”); exit(0);} bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error(“ERROR on connecting”); printf(“Please enter the message: “); bzero(buffer, 256); fgets(buffer, 255, stdin); n = write(sockfd, buffer, strlen(buffer)); if (n < 0) error (“ERROR writing to socket”); n = read(sockfd, buffer, 255); if (n < 0) error(“ERROR reading from socket”); printf(“%s\n”, buffer); close(sockfd); return 0; } The variable argv[1] contains the name of a host on the Internet, e.g. cs.rpi.edu. The function: struct hostent *gethostbyname(char *name) Takes such a name as an argument and returns a pointer to a hostent containing information about that host. The field char *h_addr contains the IP address. This code sets the fields in serv_addr. Much of it is the same as in the server. However, because the field server->h_addr is a character string, we use the function: void bcopy(char *s1, char *s2, int length) which copies length bytes from s1 to s2 The connect function is called by the client to establish a connection to the server. It takes three arguments, the socket file descriptor, the address of the host to which it wants to connect (including the port number), and the size of this address. This function returns 0 on success and -1 if it fails Notice that the client needs to know the port number of the server, but it does not need to know its own port number. This is typically assigned by the system when connect is called. The remaining code should be fairly clear. It prompts the user to enter a message, uses fgets() to read the message from stdin, writes the message to the socket, reads the reply from the socket, and displays this reply on the screen.
43
Reference tutorial/ ftp://ftp.sas.com/techsup/download/SASC/share /S5958v2.pdf Beej's Guide to Network Programming Using Internet Sockets Sockets Tutorial
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.