• 沒有找到結果。

4.5 Implementation of RTSP Messages and State Changes

4.5.1 OverallListenerThread

}STREAMING_DATA;

Note that a lot of variables contained in this structure are omitted for simplicity. This structure contains a mutex named “hRTSP_STREAMER_Mutex” and “controlData”, which is the second structure to be introduced:

typedef struct _RTSP_STREAMER{

int cmdis;

int states;

}RTSP_STREAMER;

This structure contains two integers, “cmdis” and “states”. Unsurprisingly,

“STREAMING_DATA” is the shared memory shared by the two threads, and

“hRTSP_STREAMER_Mutex” is used to protect this shared memory from being used by the two simultaneously. In addition, “cmdis” represents the latest received RTSP request and “states” is the current RTSP state. For the same example mentioned above, in

“handle_server_event()”, “RTSPProcessorThread()” will lock

“hRTSP_STREAMER_Mutex” first, avoiding “StreamerletIs()” to use the

“STREAMING_DATA” at the same time. After acquiring the mutex,

“RTSPProcessorThread()” will change “cmdis” to the PLAY request and “states” from READY to the PLAY state. “StreamerletIs()”, upon sensing this change by reading the shared memory (mutual exclusion guaranteed by the mutex), will start the packetization process.

4.5.1 OverallListenerThread

According to RTSP [3], RTSP typically runs over a reliable connection based transport level protocol such as TCP. Therefore, to be more compliant with other streaming solutions, in the proposed system, TCP is chosen to be the channel for the RTSP communication between the client and the server. Since TCP is connection-oriented (i.e. the connection between two ends has to be setup before the actual transmission takes place), the setup procedures must be taken care of. This is the main job of “OverallListenerThread()”. It assists the server to accept an incoming

connection attempt of a client on the designated socket. After this connection is setup successfully, it passes the control of this connection to another thread whose job is to receive and reply RTSP messages, and starts to wait for another new incoming connection.

In the proposed system, on the server side, the socket bound to port 554 is specially opened to listen for an incoming connection from the client. To bind this socket with port 554 and let it start listening, the following code is executed:

struct sockaddr_in s_in;

int fd = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);

s_in.sin_family = AF_INET;

s_in.sin_addr.s_addr = INADDR_ANY;

s_in.sin_port = htons(554);

bind(fd, &s_in, sizeof(struct sockaddr));

listen(fd, 5);

The second line creates a socket file descriptor named “fd”, which is used to handle a socket, and binds it to a specific transport service provider. In this case, the first parameter is the address family specification and set “AF_INET” by default. The second and the third parameters indicate that this socket is a reliable two-way connected communication stream (TCP). Now it is time to bind this socket to a port on the local machine. As can be observed from the code, “s_in” is a pointer to a structure named

“sockaddr_in” that contains information about the port and IP address. “INADDR_ANY”

denotes the IP address of this machine. The port, as desired, is set to 554. The second last line then binds “fd” to port 554 using “s_in”. The last line, finally, makes “fd” socket listen for an incoming connection.

After letting the “fd” start listening on port 554, “OverallListenerThread()” can begin its job. The following few lines summarize the job that this thread is working on:

while(server is running){

struct sockaddr_in client_addr;

int sin_size = sizeof(struct sockaddr_in);

int client_fd = accept(fd,&client_addr,&sin_size);

/*Creates an instance of RTSPProcessorThread() to handle this established

connection (i.e. passing client_fd to RTSPProcessorThread()).*/

}

Basically, as long as the server is up and running, this thread is listening on port 554 to wait for a new incoming connection from the client. After a connection is accepted, a new socket file descriptor (named “client_fd”) will be created to take over the control of this connection (instead of the original “fd”). The original socket file descriptor will still be listening on port 554 and will accept the next new incoming connection by calling

“accept()” function call again. This is done by placing these lines in a while loop. On the other hand, the newly created one will be passed as a parameter to

“RTSPProcessorThread()”, who will use function calls like “send()” and “recv()” to perform RTSP message exchanges. According to [20], in this case, “fd” are “client_fd”

are called “listening socket” and “connected socket”, respectively, since “fd” during its life time, always listens to new incoming connection, while “client_fd”, on the other hand is created and used to perform actual data transmission when a connection is successfully setup.

As a side note, most of TCP socket related structures and functions employed in the implementation, such as “struct sockaddr_in”, “socket()”, “bind()”, “connect()”, “listen()”,

“accept()”, “send()”, and “recv()”, are contained in the Windows® Socket library. They can be used directly after including wsock32.lib and winsock.h in the program.

OverallListenerThread()

Figure 33 – Illustration of multiple RTSP sessions connecting to a server [20]

Figure 33 [20] is a typical scenario when a client creates multiple streaming sessions with a single server. It illustrates how the server deals with multiple socket file descriptors bound to the same port (554). Initially, on the server side, “OverallListenerThread()” will call “socket()”, “bind()”, and “listen()” to create a socket that listens to incoming connection from the client. As mentioned above, this socket is called “listening socket”

[20]. At this time, since no connection has been setup on port 554, any incoming connection attempt with any source/destination pair will be handled by this socket without a doubt. When the client on the left hand side creates Session 1 and calls “connect()” to connect to the server, the server will call “accept()” to accept this connection. “accept()”

function, upon executing successfully, will return a new socket handler to take over the control of this connection. So this new socket is called “connected socket” [20], as described earlier. Therefore, on the server side, both the original socket and the newly created socket are now bound to port 554. If again, the client creates another session and connects to the server via port 554 on the server machine, then the server will have the original socket, and two newly created sockets bound to port 554. How the server distinguishes the packets sent from Session 1 and Session 2 which are of same IP address, and how it differentiates the data packets and connecting requests from new clients, are described below. Note that the wildcard notation within the braces beneath

“OverallListenerThread()” denotes that the listening socket listens to any incoming connection with any source address other than ones already managed by other sockets.

Therefore, if a packet from 140.100.1.1:4000 and destined to 140.113.13.81:554 will not be handled by this listening socket. Indeed, it will be captured by the connected socket of Session 1 since its source/destination pair matches that of this socket. In summary, even though multiple socket file descriptors are bound to 554, any incoming packet or connecting request will not be captured by the wrong socket since source/destination pair information contained in the packet will enable the underlying kernel program to deliver the packet to the correct socket.

相關文件