I think I understand the question you're asking. Is it:
I have a client-server system in which the client can have multiple requests pending simultaneously. When the client receives a response, how can it tell which request corresponds to this response?
If so, there are several ways to proceed. Note that in the following discussion we distinguish between the protocol itself vs. the programming interface (API) that applications use to drive the protocol.
0. Use an established protocol.
There are many subtleties involved in developing protocols. Existing protocols such as HTTP may have solved these issues satisfactorily for your application already, and there are many APIs for it already available. I'm with BicycleDude, use an existing protocol if possible. However, if you can't use an existing protocol for some reason, read on.
1. Make the protocol and API fully synchronous.
This is the simplest approach. When the client issues a request, it blocks reading from the socket until it receives the response -- the entire response. Then it returns the response to the caller. This is changing the protocol so that there is at most one request pending at any given time. The response received unambiguously corresponds to the request just sent. The API is simple, because the caller issues the request and server's response can be the return value of the function called to issue the request.
The problem is that it's slow. Each request suffers round-trip latency to the server. The client can't do anything else while it's waiting. (Unless the client is multi-threaded.)
You should seriously consider this approach (if you've decided not to reuse an existing protocol). This is similar in style to RPC (Remote Procedure Calls). The latency issues can be irritating, but the implementation and programming model are simple. If you decide to step away from being fully synchronous, things get complicated very fast.
2. Allow pipelining in the protocol, and make the API asynchronous.
"Pipelining" means that multiple requests can be pending, but that the server is required to process and to respond to each request in order. The client keeps track of which requests are pending and, since the responses come in order, it knows which request corresponds to which response.
There is great diagram on the Wikipedia article for HTTP pipelining. See, you really should consider using HTTP. :-)
The programming model gets more complicated now. When the client issues a request Request1, the call returns immediately and doesn't wait for the corresponding response Response1. Now the application can continue doing other work and eventually issue Request2 in the same way. How does the application get its responses? The usual way is for the API to return some object that represents the pending request. When the application wants to get the response for that request, it makes a call on that object. The pseudo-code would look something like this:
request1 = send_request(...)
response1 = request1.get_response()
Now, note that the application can ask for responses in a different order from which the requests were issued:
request1 = send_request(...)
request2 = send_request(...)
response2 = request2.get_response()
How does this work? The client will receive response1 over the network first, but that's not what the application is asking for -- it's asking for response2. So the client (presumably somewhere in a library) will have to buffer up response1 and then read, possibly waiting for, response2 to come in. Now we have a situation where the library needs to buffer up a potentially arbitrary amount of responses until the application asks for those specific responses. If the application forgets to do so, well, that's a memory leak. The library may also need to cancel a pending request; this doesn't really cancel it, but basically just tells the library to discard that particular response.
3. Make the protocol asynchronous.
This variation allows the server to respond to requests out-of-order. This could happen if the server processes its requests using a thread pool. Whenever a server thread completes its work, it sends its response, regardless of the order in which the requests were received.
So, the client might issue request1 and then request2, but the server might send response2 first. This variation has the same client-side buffering and request-response tracking issues, but now the protocol itself has to keep track of which response corresponds to which request. The usual way to do this is with sequence numbers or IDs. The client sends a sequence number along with every request. This number must be unique, at least unique enough so that no two pending requests ever have the same ID. Then, the server has to put this ID number in every response.
Still other variations are possible, for example, allowing the server to send zero or more than one response to each request, or allowing the server to send messages spontaneously (that is, not in response to any particular request).