430 likes | 581 Views
Chapter 10. SCTP Client / Server example. Simple echo server using SCTP protocol Send line of text from client to server Server sends the same line back to the client Iterative server. Client. Client reads a single line of text from standard input Text is send in [#]text format.
E N D
Chapter 10 SCTP Client / Server example
Simple echo server using SCTP protocol • Send line of text from client to server • Server sends the same line back to the client • Iterative server
Client • Client reads a single line of text from standard input • Text is send in [#]text format. • # represents SCTP stream number where text is sent
Server • Reads the text from the network • Increases the SCTP stream number by one (# in [#]text) • Sends the message back to the client on this new stream
Client • Reads the reply message and prints it to standard output • Stream number, sequence number and text string is printed
#include "unp.h" • int • { • int sock_fd,msg_flags; • char readbuf[BUFFSIZE]; • struct sockaddr_in servaddr, cliaddr; • main(int argc, char **argv) • struct sctp_sndrcvinfo sri; • struct sctp_event_subscribe evnts; • int stream_increment=1; • socklen_t len; • size_t rd_sz; • if (argc == 2) • stream_increment = atoi(argv[1]); • sock_fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); • bzero(&servaddr, sizeof(servaddr)); • servaddr.sin_family = AF_INET; • servaddr.sin_addr.s_addr = htonl(INADDR_ANY); • servaddr.sin_port = htons(SERV_PORT); • Bind(sock_fd, (SA *) &servaddr, sizeof(servaddr)); • bzero(&evnts, sizeof(evnts)); • evnts.sctp_data_io_event = 1; • Setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, • &evnts, sizeof(evnts)); • Listen(sock_fd, LISTENQ); • for ( ; ; ) { • len = sizeof(struct sockaddr_in); • rd_sz = Sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf), • (SA *)&cliaddr, &len, • &sri,&msg_flags); • if(stream_increment) { • sri.sinfo_stream++; • if(sri.sinfo_stream >= sctp_get_no_strms(sock_fd,(SA *)&cliaddr, len)) • sri.sinfo_stream = 0; • } • Sctp_sendmsg(sock_fd, readbuf, rd_sz, • (SA *)&cliaddr, len, • sri.sinfo_ppid, • sri.sinfo_flags, • sri.sinfo_stream, • 0, 0); • } • }
Server: Stream increment • Stream increment option • If command line option is passed to program, program decides whether to increment stream number of incoming messages • if (argc == 2) • stream_increment = atoi(argv[1]);
Server: Create an SCTP socket • sock_fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
Server: Bind address • Bind any incoming address to certain server port. • bzero(&servaddr, sizeof(servaddr)); • servaddr.sin_family = AF_INET; • servaddr.sin_addr.s_addr = htonl(INADDR_ANY); • servaddr.sin_port = htons(SERV_PORT); • Bind(sock_fd, (SA *) &servaddr, sizeof(servaddr));
Server: Set up for notification • Server changes its notification subscription for the one-to-many SCTP socket. • This allows server to see the stream number where the message arrived. • bzero(&evnts, sizeof(evnts)); • evnts.sctp_data_io_event = 1; • Setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts));
Server: Incoming associations • Server enables incoming associations so it can listen the server socket for incoming messages. • Listen(sock_fd, LISTENQ); • After which server will enter the main loop
Server: Wait for messages • Initialize size of client socket address structure. • Block until message arrives. • len = sizeof(struct sockaddr_in); • rd_sz = Sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf), (SA *)&cliaddr, &len, &sri,&msg_flags);
Server: Increment stream number • Check increment flag and increase stream stream number is flag is set. • If number is too large then number is set to 0. • if(stream_increment) { • sri.sinfo_stream++; • if(sri.sinfo_stream >= sctp_get_no_strms(sock_fd, (SA *)&cliaddr, len)) • sri.sinfo_stream = 0; • }
Server: Send message back • Sending back the message to the client • Sctp_sendmsg(sock_fd, readbuf, rd_sz, • (SA *)&cliaddr, len, • sri.sinfo_ppid, • sri.sinfo_flags, • sri.sinfo_stream, • 0, 0);
Server: Finish? • Program runs forever until it is shutdown with an external signal. • for ( ; ; ) { • … • }
#include "unp.h" • int • main(int argc, char **argv) • { • int sock_fd; • struct sockaddr_in servaddr; • struct sctp_event_subscribe evnts; • int echo_to_all=0; • if(argc < 2) • err_quit("Missing host argument - use '%s host [echo]'\n", • argv[0]); • if(argc > 2) { • printf("Echoing messages to all streams\n"); • echo_to_all = 1; • } • sock_fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); • bzero(&servaddr, sizeof(servaddr)); • servaddr.sin_family = AF_INET; • servaddr.sin_addr.s_addr = htonl(INADDR_ANY); • servaddr.sin_port = htons(SERV_PORT); • Inet_pton(AF_INET, argv[1], &servaddr.sin_addr); • bzero(&evnts, sizeof(evnts)); • evnts.sctp_data_io_event = 1; • Setsockopt(sock_fd,IPPROTO_SCTP, SCTP_EVENTS, • &evnts, sizeof(evnts)); • if(echo_to_all == 0) • sctpstr_cli(stdin,sock_fd,(SA *)&servaddr,sizeof(servaddr)); • else • sctpstr_cli_echoall(stdin,sock_fd,(SA *)&servaddr,sizeof(servaddr)); • Close(sock_fd); • return(0); • }
Client: Validate arguments • Arguments: host and echo flag. • if(argc < 2) • err_quit("Missing host argument - use '%s host [echo]'\n", argv[0]); • if(argc > 2) { • printf("Echoing messages to all streams\n"); • echo_to_all = 1; • }
Client: Create a socket • Create an SCTP socket • sock_fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
Client: Set up server address • Use given server address and well known port number to construct server address structure using Inet_pton(). • bzero(&servaddr, sizeof(servaddr)); • servaddr.sin_family = AF_INET; • servaddr.sin_addr.s_addr = htonl(INADDR_ANY); • servaddr.sin_port = htons(SERV_PORT); • Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Client: Set up for notifications • Explicitly setting notification subscription for one-to-many socket. • bzero(&evnts, sizeof(evnts)); • evnts.sctp_data_io_event = 1; • Setsockopt(sock_fd,IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts));
Client: Echo processing • If flag is not set then sctpstrcli() is called. • Else sctpstr_cli_echoall() is called. • if(echo_to_all == 0) • sctpstr_cli(stdin,sock_fd,(SA *)&servaddr,sizeof(servaddr)); • else • sctpstr_cli_echoall(stdin,sock_fd,(SA *)&servaddr,sizeof(servaddr)); • These functions are shown later.
Client: Finish • When all is done, client closes socket and returns zero. • Close(sock_fd); • return(0);
Client: sctpstr_cli() • #include "unp.h" • void • sctpstr_cli(FILE *fp, int sock_fd, struct sockaddr *to, socklen_t tolen) • { • struct sockaddr_in peeraddr; • struct sctp_sndrcvinfo sri; • char sendline[MAXLINE], recvline[MAXLINE]; • socklen_t len; • int out_sz,rd_sz; • int msg_flags; • bzero(&sri,sizeof(sri)); • while (fgets(sendline, MAXLINE, fp) != NULL) { • if(sendline[0] != '[') { • printf("Error, line must be of the form '[streamnum]text'\n"); • continue; • } • sri.sinfo_stream = strtol(&sendline[1],NULL,0); • out_sz = strlen(sendline); • Sctp_sendmsg(sock_fd, sendline, out_sz, • to, tolen, • 0, 0, • sri.sinfo_stream, • 0, 0); • len = sizeof(peeraddr); • rd_sz = Sctp_recvmsg(sock_fd, recvline, sizeof(recvline), • (SA *)&peeraddr, &len, • &sri,&msg_flags); • printf("From str:%d seq:%d (assoc:0x%x):", • sri.sinfo_stream,sri.sinfo_ssn, • (u_int)sri.sinfo_assoc_id); • printf("%.*s",rd_sz,recvline); • } • }
Client: Initialize • Initialize structure and enter loop. • Block until user inputs line of text. • bzero(&sri,sizeof(sri)); • while (fgets(sendline, MAXLINE, fp) != NULL) {
Client: Validate input • Validate given input from the user. • if(sendline[0] != '[') { • printf("Error, line must be of the form '[streamnum]text'\n"); • continue; • }
Client: Stream number • Stream number is translated from the user input and put into the structure. • sri.sinfo_stream = strtol(&sendline[1],NULL,0);
Client: Send message • Message is sent. • out_sz = strlen(sendline); • Sctp_sendmsg(sock_fd, sendline, out_sz, • to, tolen, 0, 0, • sri.sinfo_stream, 0, 0);
Client: Wait for response • Program blocks until message is received back from the server. • len = sizeof(peeraddr); • rd_sz = Sctp_recvmsg(sock_fd, recvline, sizeof(recvline), • (SA *)&peeraddr, &len, • &sri,&msg_flags);
Client: Display message • Received message is displayed to standard output. • printf("From str:%d seq:%d (assoc:0x%x):", • sri.sinfo_stream,sri.sinfo_ssn, • (u_int)sri.sinfo_assoc_id); • printf("%.*s",rd_sz,recvline);
Client: Finish? • Program reads lines from user until it is terminated by an external signal.
Head-of-Line blocking • Head-of-Line blocking occurs when TCP segment is lost and latter segments arrive out of order. • All the subsequent segments are held until the lost segment is retransmitted. • This ensures data arrives to the application in the order it was sent.
SCTP blocking of streams • SCTP allows multiple streams over single connection. • Each stream can be transmitted independent of other streams. • Blocking of one stream for retransmission does not block other streams.
Client: sctpstr_cli_echoall() • #include "unp.h" • #define SCTP_MAXLINE 800 • void • sctpstr_cli_echoall(FILE *fp, int sock_fd, struct sockaddr *to, socklen_t tolen) • { • struct sockaddr_in peeraddr; • struct sctp_sndrcvinfo sri; • char sendline[SCTP_MAXLINE], recvline[SCTP_MAXLINE]; • socklen_t len; • int rd_sz,i,strsz; • int msg_flags; • bzero(sendline,sizeof(sendline)); • bzero(&sri,sizeof(sri)); • while (fgets(sendline, SCTP_MAXLINE - 9, fp) != NULL) { • strsz = strlen(sendline); • if(sendline[strsz-1] == '\n') { • sendline[strsz-1] = '\0'; • strsz--; • } • for(i=0;i<SERV_MAX_SCTP_STRM;i++) { • snprintf(sendline + strsz, sizeof(sendline) - strsz, • ".msg.%d", i); • Sctp_sendmsg(sock_fd, sendline, sizeof(sendline), • to, tolen, • 0, 0, • i, • 0, 0); • } • for(i=0;i<SERV_MAX_SCTP_STRM;i++) { • len = sizeof(peeraddr); • rd_sz = Sctp_recvmsg(sock_fd, recvline, sizeof(recvline), • (SA *)&peeraddr, &len, • &sri,&msg_flags); • printf("From str:%d seq:%d (assoc:0x%x):", • sri.sinfo_stream,sri.sinfo_ssn, • (u_int)sri.sinfo_assoc_id); • printf("%.*s\n",rd_sz,recvline); • } • } • }
Client: Initialize and read input • Initialize data structures. • Enter loop and read user input. • bzero(sendline,sizeof(sendline)); • bzero(&sri,sizeof(sri)); • while (fgets(sendline, SCTP_MAXLINE - 9, fp) != NULL) {
Client: Process user input • Process given user input. • strsz = strlen(sendline); • if(sendline[strsz-1] == '\n') { • sendline[strsz-1] = '\0'; • strsz--; • }
Client: Send message • Send message to each stream. • Append .msg. and stream number to each message for visual help of tracking messages. • for(i=0;i<SERV_MAX_SCTP_STRM;i++) { • snprintf(sendline + strsz, sizeof(sendline) - strsz, • ".msg.%d", i); • Sctp_sendmsg(sock_fd, sendline, sizeof(sendline), • to, tolen, 0, 0, i, 0, 0); • }
Client: Read, display and loop • Block until incoming data comes from streams. • Display message and move to read next. • for(i=0;i<SERV_MAX_SCTP_STRM;i++) { • len = sizeof(peeraddr); • rd_sz = Sctp_recvmsg(sock_fd, recvline, sizeof(recvline), • (SA *)&peeraddr, &len, &sri,&msg_flags); • printf("From str:%d seq:%d (assoc:0x%x):", • sri.sinfo_stream,sri.sinfo_ssn, (u_int)sri.sinfo_assoc_id); • printf("%.*s\n",rd_sz,recvline); • } • After last message is read, loop back to user input.
Client: Modifications • Send two messages to each stream to see messages are being held for reordering. • for(i=0;i<SERV_MAX_SCTP_STRM;i++) { • snprintf(sendline + strsz, sizeof(sendline) - strsz, ".msg.%d 1", i); • Sctp_sendmsg(sock_fd, sendline, sizeof(sendline), • to, tolen, 0, 0, i, 0, 0); • snprintf(sendline + strsz, sizeof(sendline) - strsz, ".msg.%d 2", i); • Sctp_sendmsg(sock_fd, sendline, sizeof(sendline), • to, tolen, 0, 0, i, 0, 0); • } • for(i=0;i<SERV_MAX_SCTP_STRM*2;i++) { • len = sizeof(peeraddr);
Number of streams • FreeBSD KAME implementation: default 10 streams at start. • Change must be made on the socket before an association is made. • if (argc == 2) • stream_increment = atoi(argv[1]); • sock_fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); • bzero(&initm,sizeof(initm)); • initm.sinit_num_ostreams = SERV_MORE_STRMS_SCTP; • Setsockopt(sock_fd, IPPROTO_SCTP, SCTP_INITMSG, • &initm, sizeof(initm));
Termination • Two possible methods for closing one-to-many association: • Close after sending message (eof) • Close immediately (abort)
Termination: eof • Server wishes to shutdown an association after sending message • Set flag MSG_EOF in the reply message structure. • Sctp_sendmsg(sock_fd, readbuf, rd_sz, • (SA *)&cliaddr, len, sri.sinfo_ppid, • (sri.sinfo_flags | MSG_EOF), sri.sinfo_stream, 0, 0); • }
Termination: abort • Server wishes to shutdown an association immediately. • Set flag MSG_ABORT in the reply message structure. • Forces immediate termination with an ABORT chunk. • Any data not transmitted will be discarded. • strcpy(byemsg,"goodbye"); • Sctp_sendmsg(sock_fd, byemsg, strlen(byemsg), • (SA *)&servaddr, sizeof(servaddr), 0, MSG_ABORT, 0, 0, 0); • Close(sock_fd); • Even if association has been aborted, socket descriptor still needs to be freed with Close().
Summary • Simple SCTP client and iterative server • Head-of-Line blocking problem • Number of streams • Shutdown and abort associations • More on SCTP on chapter 23