套接字类型与协议设置

第2章 套接字类型与协议设置

2.1 套接字协议及其数据传输特性

协议(Protocol)

协议就是一种规则,是为了完成数据交换而定的约定。

创建套接字
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//成功返回文件描述符,否则-1
// domain --- 使用的协议族信息
// type   --- 数据传输类型信息
// protocol - 通信时用的协议信息
协议族(Protocol Family)

头文件sys/socket.h中声明的协议族

名称协议族
PF_INETIPV4互联网协议族
PF_INET6IPV6互联网协议族
PF_LOCAL本地通信的UNIX协议族
PF_PACKET底层套接字的协议族
PF_IPXIPX Novell的协议族

套接字中,实际采用的最终协议信息是通过第三个参数传递的。但是第一个参数决定着第三个参数。

套接字类型1:面向连接的套接字(SOCK_STREAM)

类似一个传送带

这是面向连接的数据传输:

1、 传输过程中数据不会消失

2、 按序传输数据

3、 传输的数据不存在数据边界(Boundary)

如何理解数据边界呢?

比如:计算机通过三次write传输了100字节的数据,但是接收数据的计算机仅通过一次read就接收了100字节的数据。

因为收发数据的套接字内部由缓冲(一个字节数组),因此数据也保存到该数组中。因此只要不超过数组容量,read函数和write函数调用次数没什么意义。

即使缓冲已满,也不代表数据丢失。如果read比接受数据的速度慢,则有可能会被填满。此时无法接受数据,但不意味着丢失,因为传输套接字将会停止传输。即面向连接的套接字会根据接收端的状态传输数据,如果传输出错还会重传。因此除特殊情况外,面向连接的套接字不会发生数据丢失。

需要注意:套接字必须一一对应。

总的来说:可靠的、按序传递的、基于字节面向连接的数据传输方式的套接字。

套接字类型2:面向消息的套接字(SOCK_DGRAM)

面向消息的套接字可以比喻为高速移动的摩托车快递。

1、 强调快速传输而非传输顺序。

2、 传输的数据可能丢失也可能损毁。

3、 传输的数据可能丢失也可能损毁。

4、 限制每次传输的数据大小。

比如:每次包裹(数据包)的大小有一定的限制,如果传递大量包裹则需要分批发送。如果用两辆摩托车运送快递,则客户也需要分两次接收。这就是传输的数据具有数据边界

总结:不可靠的、不按序传递的、以数据的高速传输为目的的套接字。

协议的最终选择

第三个参数决定最终采用的协议。尽管我们已经通过第一个参数设置协议族信息,第二个参数设置套接字数据传输方式,但是大家要知道:同一协议族中存在多个数据传输方式相同的协议

所以数据传输方式相同,但是协议不同。

基于IPv4面向连接,只有IPPROTO_TCP协议满足。该套接字称为TCP套接字。

int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

SOCK_DGRAM指的是面向消息的数据传输方式,满足上述条件的协议只有IPPROTO_UDP这种套接字称为UDP套接字。

int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
面向连接的套接字:TCP套接字示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

void error_handling(char *message);

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

    if(argc != 3){
        printf("Usage: %s <IP> <port> \n", argv[0]);
        exit(1);
    }
    //若前两个参数是PF_INET,SOCK_STREAM则可省略第三个参数
    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*) &serv_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;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n',stderr);
    exit(1);
}

2.2 Windows平台下的实现及验证

因为套接字类型及传输特性与操作系统无关,所以只需要了解返回类型即可。

Windows操作系统的socket函数

Windows和Linux的函数名都相同,只是返回值不同

#inclde <winsock2.h>
SOCKET socket(int af, int type, int protocol);
//成功返回句柄,否则返回INVALID_SOCKET(实质上是-1)
基于Windows的TCP套接字示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

void error_handling(const 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)
        error_handling("WSAStartup() error");
    hSocket = socket(PF_INET, SOCK_STREAM, 0);
    if (hSocket == INVALID_SOCKET)
        memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = inet_addr(argv[1]);
    servAddr.sin_port = htons(atoi(argv[2]));

    if (connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) {
        error_handling("connect() error");
    }
	//和上方类似,主要体现的就是没有数据边界。
    while (readLen = recv(hSocket, &message[idx++], 1, 0)) {
        if (readLen == -1)
            error_handling("read() error");
        strLen += readLen;
    }
    printf("Messagee from server: %s \n", message);
    printf("Function read call count: %d \n", strLen);
    closesocket(hSocket);
    WSACleanup();
    return 0;
}

void error_handling(const char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值