TCP/IP Network Programming Chapter 2: Socket Type and Protocol Settings

In the previous chapter we recognized and practiced the process of creating a socket. Next, we will explore the various details of creating a socket in more detail.

We will create the socket with the following function:

#include<sys/socket.h>
int socket(int domain,int type,int protocol);//成功时返回文件描述符,失败时返回-1
           domain  //套接字中使用的协议族信息
           type    //套接字数据传输类型信息
           protocol//计算机间通信中使用的协议信息 

For the problems that the above parameters may not be remembered, in fact, just understand it. As we mentioned in the previous chapter, sockets are the beginning of Internet communication. In operating systems, they usually appear in the abstract form of files. So since it is a network connection, it must conform to the TCP/IP protocol stack. So since it is the beginning of the network connection, we must specify the transport layer, network layer and link layer used, otherwise the next data communication will not know where to start.

Protocol Family

The protocol family declared in the header file sys/socket.h

name protocol family
PF_INET IPv4 Internet protocol suite
PF_INET6 IPv6 Internet protocol suite
PF_LOCAL  UNIX protocol family for local communication
PF_PACKET Protocol family for underlying sockets
PF_IPX IPXNovell protocol family

In addition, the final protocol information actually adopted in the socket is passed through the third parameter of the socket function. The third parameter is determined by the first parameter within the scope of the specified protocol family.

Socket type (Type)

People who have studied the TCP/IP protocol stack can understand the reason for the socket type parameter in this way. If the first parameter defines the content of the network layer, then the content of the transport layer must also be defined. Furthermore, it is determined that the protocol family cannot determine the data transmission method at the same time. In other words, there are multiple data transmission methods in the PF_INET protocol family, the first parameter of the socket function.

Socket type 1: Connection-oriented socket (SOCK_STREAM)

Feature 1: Data will not disappear during data transmission

Feature 2: Transfer data in sequence

Feature 3: There is no data boundary in the transmitted data

Feature 4: Socket connections must correspond one-to-one

Since the determination of the socket type can be understood as the determination of the transport layer type, then connection-oriented can be understood as the transport layer protocol using TCP, then the above characteristics can be memorized through the characteristics of TCP.

It may be a bit difficult to understand the third feature, just take an example, "the computer that transmits data passes 100 bytes of data by calling the write function three times, but the computer that receives the data receives all 100 bytes by calling the read function only once", that is to say, in a connection-oriented socket, the number of calls to the read function and the write function does not have much impact.

Summarize the above characteristics in one sentence: reliable, in-order delivery, byte-based connection-oriented socket for data transmission.

Socket type 2: Message-oriented socket (SOCK_DGRAM)

Feature 1: Emphasize fast transmission rather than transmission order

Feature 2: The transmitted data may be lost or destroyed

Feature 3: The transmitted data has data boundaries

Feature 4: Limit the data size of each transfer

The above characteristics are also the characteristics of the UDP protocol. The meaning of feature 3 is that one write corresponds to one read.

The characteristics of message-oriented sockets are summarized as follows: unreliable, out-of-order delivery, sockets for high-speed data transmission.

final choice of agreement

The presence of the third parameter is optional in some cases. Its function is that if the specific type of the socket cannot be determined after the first parameter and the second parameter are determined, then its function is to finalize the protocol at this time. Based on the content mentioned above, if I want to create a "connection-oriented socket in the IPv4 protocol family", the parameter PF_INET refers to the IPv4 network protocol family, and SOCK_STREAM is connection-oriented data transmission. The protocol that satisfies these two conditions is only IPPROTO_TCP, so the socket function can be called as follows to create a socket, which is called a TCP socket.

int tcp_socket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)

In most cases you can pass 0 for the third parameter.

Connection-Oriented Sockets: TCP Sockets Example

In the previous chapter, both the server and the client were examples based on TCP sockets. Now adjust a part of the code to verify the third characteristic of TCP sockets. To verify this, the number of calls to the write function needs to be different from the number of calls to the read function. Therefore, the read function is called multiple times in the client to receive all the data sent by the server.

"头文件和上一章相同,故省略"
void error_handling(char*message);

int main(int argc,char*argv[]){
    int sock;
    struct sockaddr_in serv_addr;
    char message[30];
    int str_len=0;
    itn idx=0,read_len=0;

    if(argc!=3){
        printf("Usage : %s <IP> <port>\n",argv[0]);
        exit(1);
    }

    sock=socket(PF_INET,SOCK_STREAM,0);
    if(sock==-1)error_handling("socket() error");
    
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_addr.sin_port=htons(atoi(argv[2]));

    if(connect(sock,(struct sockaddr*)&serc_addr,sizeof(serv_addr))==-1);
    error_handling("connect() error!");

    while(read_len=read(sock,&message[idx++],1)){
          if(read_len==-1)error_handling("read() error!");
          str_len+=read_len;
    }

    printf("Message from server: %s \n",message);
    printf("Function read call count: %d \n",str_len);
    close(sock);
    return 0;
}

error_handling(char*message){
    //与上一章代码相似
}

According to the running results, the server calls the write function once to pass 13 bytes of content, while the client calls the read function 13 times to read 13 bytes of content.

Realization and verification under Windows platform

The socket types and transmission characteristics mentioned earlier have nothing to do with the operating system. The implementation method under the Windows platform is also similar, and there is no
need to explain too much, just a little understanding of the return type of the socket function.

#include <winsock2.h>
SOCKET socket(int af, int type, int protocol); //成功时返回 socket 句柄,失败时返回 INVALID_SOCKET

The parameter types and meanings of this function are exactly the same as the socket function of Linux, so they are omitted, and only the return value type is discussed. It can be seen that the return value type is SOCKET, and this structure is used to save the integer socket handle value. In fact, the socket function returns integer data, so it can be received through an int variable, just like it is done in Linux. However, considering the future scalability, it is defined as the SOCKET data type. I hope that you can also use the SOCKET structure variable to save the socket handle, which is what Microsoft hopes to
see. From now on, SOCKET can be regarded as a data type that holds a socket handle.

Similarly, INVALID_SOCKET is returned when an error occurs, just understand it as a constant that prompts an error. Its actual
value is -1, but it doesn't matter whether the value is -1 or not, unless you write the following code.

SOCKET soc = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(soc ==-1 )
ErrorHandling("...");

If the code is written like this, the INVALID_SOCKET constant defined by Microsoft will lose its meaning! should be written as follows,

SOCKET soc = socket(PF_INET, SOCK_STREAM,IPPROTO_TCP);
if ( soc == INVALID_SOCKET )
ErrorHandling("....");

Windows-based TCP socket demo

As before, only the source code and running results of the client are given.

#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
void ErrorHnadling(char*message);

int main(int argc,char*argv[]){
    WSADATA wsaData;
    SOCKET hSocket;
    SOCKADDR_IN servAddr;
     
    char message[30];
    int strlen=0;
    int idx=0,readLen=0;

    if(argc!=3){
        printf("Usage : %s <IP> <port>\n",argv[0]);
        exit(1);
    }

    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)ErrorHandling("WSAStartup() error!");

    hSocket=socket(PF_INET,SOCK_STREAM,0);
    if(hSocket==INVALID_SOCKET)ErrorHandling("hSocket() error");

    memset(&servAddr,0,sizeof(servAddr));
    servAddr.sin_family=AF_INET;
    servAddr.sin_addr.s_addr=inet_addr(argv[1]);
    setvAddr.sin_port=htons(atoi(argv[2]));

    if(connect(hSocket,(SOCKADDR*)&servAddr,sizeof(servAddr))==SOCKET_ERROR)
    ErrorHandling("connect() error!");

    while(readLen=recv(hSocket,&message[idx++],1,0)){
         if(readLen==-1)ErrorHandling("read() error!");
         strLen+=readLne;if(message[idx-1]=='\0')break;
    }

    printf("Message from server: %s \n",message);
    printf("Function read call count: %d \n",strLen);
    
    closesocket(hSocket);
    WSACleanup();
    return 0;
}

void ErrorHandling(char*message){
    fput(message,strerr);
    fputs('\n',stderr);
    exit(1);
}

Guess you like

Origin blog.csdn.net/Reol99999/article/details/131611828