• 《Linux C/C++服务器开发实践》之第4章 TCP服务器编程


    4.1 套接字的基本概念

    套接字是TCP/IP网络编程中的基本操作单元,不同主机的进程之间的相互通信的端点。
    socket是在应用层和传输层之间的一个抽象层,它把TCP/IC层复杂的操作抽象为几个简单的接口,供应用层调用已实现进程在网络中通信。

    4.2 网络程序的架构

    B/S(Browser/Server,浏览器/服务器)架构,用户只需要浏览器就行,主要逻辑在服务器完成,减轻了客户端的升级和维护的工作量。

    C/S(Client/Server,客户机/服务器)架构,客户端和服务端安装不同应用软件,客户端软件安装或升级比较复杂,维护成本大,可以充分利用两端的硬件能力,较为合理的分配任务。

    客户机和服务器间的通信过程:
    (1)客户机向服务器提出一个请求。
    (2)服务器收到客户机的请求,进行分析处理。
    (3)服务器将处理的结果返回给客户机。

    4.3 IP地址的格式转换

    #include 
    //字符串IP地址转换为网络字节序存储在addr中,并返回该网络字节序表示的无符号整数。
    //失败:返回0
    int inet_aton(const char *IP, struct in_addr *addr);
    
    //返回网络字节序
    //uint32,失败-1
    in_addr_t inet_addr(const char* cp);
    
    //及时复制返回的字符串
    char *inet_ntoa(struct in_addr in);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    #include 
    #include 
    #include 
    
    int main()
    {
    	char IP[] = "159.12.8.109";
    	in_addr address;
    	int number = inet_aton(IP, &address);//将点分十进制的IP地址转化为二进制的网络字节序
    	if(number == 0)
    	{
    		std::cerr<<"error IP!";
    		exit(1);
    	}
    	std::cout << number << std::endl;
    	std::cout << inet_ntoa(address) << std::endl;//将网络字节序地址转化为点分十进制表示形式
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    4.1.c
    #include 
    #include 
    
    int main()
    {
    	struct in_addr ia;
    	ia.s_addr = inet_addr("172.16.2.6");
    
    	printf("ia.s_addr = 0x%x\n", ia.s_addr);
    	printf("real_ip = %s\n", inet_ntoa(ia));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.4 套接字的类型

    (1)流套接字(SOCK_STREAM)
    用于提供面向连接的、可靠的数据传输服务,无数据边界(收发次数不一致),可保证数据能够无差别、无重复发送,并按顺序接收(因为使用了传输控制协议即TCP)。

    (2)数据报套接字(SOCK_DGRAM)
    提供无连接的服务,不保证数据传输的可靠性,有数据边界(数据报收发次数一致),数据传输过程中可能丢失或重复,且无法保证接收数据有序。(使用UDP传输)

    (3)原始套接字(SOCK_RAW)
    允许对较低层次的协议(IP、ICMP等)直接访问,常用于检验新的协议实现,或者访问现有服务中配置的新设备。
    能够控制网络底层传输机制,所以可以应用原始套接字操纵网络层和传输层应用。接收ICMP、IGMP协议包,接收TCP/IP栈不能处理的IP包,发送自定义报头或自定义协议的IP包。
    能够读写内核没有处理的IP数据报。

    4.5 套接字地址

    包含IP地址和端口信息,识别主机及进程。

    4.5.1 通用socket地址

    表示大多数网络地址,用于socket API函数中。

    struct sockaddr{
    	sa_family_t sa_family;
    	char sa_data[14];
    };
    
    • 1
    • 2
    • 3
    • 4

    sa_family_t:地址族或协议族类型

    • PF_UNIX: UNIX本地域协议族
    • PF_INET: IPv4协议族
    • PF_INET6: IPv6协议族
    • AF_UNIX: UNIX本地域地址族
    • AF_INET: IPv4地址族
    • AF_INET: IPv6地址族

    sa_data:存放具体的地址数据(IP和端口)

    协议族地址的含义和长度
    PF_INET32位IPV4地址和16位端口号,共6字节
    PF_INET6128位IPv6地址、16位端口号、32位流标识和32位范围ID,共26字节
    PF_UNIX文件全路劲名,最大长度可达108字节

    4.5.2 专用socket地址

    不同协议族定义的不同socket地址结构体,各个信息用不同字段表示。
    一般强制转换为通用地址结构使用。

    /* Structure describing an Internet socket address.  */
    struct sockaddr_in
      {
        __SOCKADDR_COMMON (sin_);
        in_port_t sin_port;			/* Port number.  */
        struct in_addr sin_addr;		/* Internet address.  */
    
        /* Pad to size of `struct sockaddr'.  */
        unsigned char sin_zero[sizeof (struct sockaddr)
    			   - __SOCKADDR_COMMON_SIZE
    			   - sizeof (in_port_t)
    			   - sizeof (struct in_addr)];
      };
    
    /* Internet address.  */
    typedef uint32_t in_addr_t;
    struct in_addr
      {
        in_addr_t s_addr;
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    #if !__USE_KERNEL_IPV6_DEFS
    /* Ditto, for IPv6.  */
    struct sockaddr_in6
      {
        __SOCKADDR_COMMON (sin6_);
        in_port_t sin6_port;	/* Transport layer port # */
        uint32_t sin6_flowinfo;	/* IPv6 flow information */
        struct in6_addr sin6_addr;	/* IPv6 address */
        uint32_t sin6_scope_id;	/* IPv6 scope-id */
      };
    #endif /* !__USE_KERNEL_IPV6_DEFS */
    
    
    #if !__USE_KERNEL_IPV6_DEFS
    /* IPv6 address */
    struct in6_addr
      {
        union
          {
    	uint8_t	__u6_addr8[16];
    	uint16_t __u6_addr16[8];
    	uint32_t __u6_addr32[4];
          } __in6_u;
    #define s6_addr			__in6_u.__u6_addr8
    #ifdef __USE_MISC
    # define s6_addr16		__in6_u.__u6_addr16
    # define s6_addr32		__in6_u.__u6_addr32
    #endif
      };
    #endif /* !__USE_KERNEL_IPV6_DEFS */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    4.5.3 获取套接字地址

    #include 
    int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
    //成功0,失败-1
    //socklen_t addrlen = sizeof(struct sockaddr_in);
    
    • 1
    • 2
    • 3
    • 4

    getsockname获取本地套接字地址的情况:

    • 本地套接字已bind地址
    • 本地套接字已connet到远程,内核会分配地址
    • 套接字端口为0时,会随机分配一个端口
    #include 
    int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
    //成功0,失败-1
    //socklen_t addrlen = sizeof(struct sockaddr_in);
    
    • 1
    • 2
    • 3
    • 4
    4.2.c
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
    	int sfp = socket(AF_INET, SOCK_STREAM, 0);
    	if (-1 == sfp)
    	{
    		printf("socket() fail!\n");
    		return -1;
    	}
    	printf("socket() ok!\n");
    
    	char on = 1;
    	setsockopt(sfp, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    	struct sockaddr_in serv = {0};
    	int serv_len = sizeof(serv);
    	printf("ip=%s, port=%d\n", inet_ntoa(serv.sin_addr), ntohs(serv.sin_port));
    
    	getsockname(sfp, (struct sockaddr *)&serv, (socklen_t *)&serv_len);
    	printf("ip=%s, port=%d\n", inet_ntoa(serv.sin_addr), ntohs(serv.sin_port));
    
    	unsigned short portnum = 10051;
    	struct sockaddr_in s_add;
    	memset(&s_add, 0, sizeof(struct sockaddr_in));
    	s_add.sin_family = AF_INET;
    	s_add.sin_addr.s_addr = inet_addr("127.0.0.1");
    	s_add.sin_port = htons(portnum);
    
    	if (-1 == bind(sfp, (struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
    	{
    		printf("bind() fail: %d!\n", errno);
    		return -1;
    	}
    	printf("bind() ok!\n");
    
    	getsockname(sfp, (struct sockaddr *)&serv, (socklen_t *)&serv_len);
    	printf("ip=%s, port=%d\n", inet_ntoa(serv.sin_addr), ntohs(serv.sin_port));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    4.6 主机字节序和网络字节序

    • 小端字节序Little Endian
      底地址存放低字节

    大端字节序Big Endian(网络字节序)
    底地址存放高字节

    uint32_t主机字节序转网络字节序
    htonl
    uint16_t主机字节序转网络字节序
    htons
    
    uint32_t网络字节序转主机字节序
    ntohl
    uint16_t网络字节序转主机字节序
    ntohs
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    4.3.cpp
    #include 
    using namespace std;
    
    int main()
    {
    	int nNum = 0x12345678;
    	char *p = (char *)&nNum;
    	if (*p == 0x12)
    		cout << "This machine is big endian." << endl;
    	else
    		cout << "This machine is small endian." << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.7 协议族和地址族

    • 协议族
      不同协议的集合,宏以PF_开头,PROTOCOL FAMILY

    • 地址族
      协议族所使用的地址集合(不同网络协议使用不同网络地址),宏以AF_开头,Address Family

    地址族和协议族的值一样,都用来标识不同的一套协议。

    4.8 TCP套接字编程的基本步骤

    • 服务器编程步骤:
      一、创建套接字,socket函数
      二、绑定套接字到IP地址和端口,bind函数
      三、套接字设置为监听模式并等待连接请求,listen函数
      四、请求到来时,接受连接请求,返回对应连接的套接字,accept函数
      五、用该连接套接字同客户端通信,send或recv函数,通信结束关闭,closesocket函数
      六、监听套接字等待其他客户端的连接
      七、推出服务器程序,关闭监听套接字,closesocket函数

    • 客户端编程步骤:
      一、创建套接字,socket函数
      二、向服务器发出请求连接,connect函数
      三、同服务器端通信,send或recv函数,
      四、通信结束关闭,closesocket函数

    4.9 TCP套接字编程的相关函数

    4.9.1 BSD socket的头文件

    • :核心函数和数据结构的声明
    • :地址族和协议族,IP地址和端口号等
    • :地址族和协议族的宏定义
    • :IP地址相关函数
    • :协议名和主机名转化为数字的函数
    #include 
    #include 
    #include 
    #include 
    #include 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.9.2 socket函数

    创建套接字,并分配系统资源

    int socket(int domain, int type, int protocol);
    //AF_INET
    //SOCK_STREAM, SOCK_DGRAM, SOCK_RAW
    //IPPROTO_TCP, IPPROTO_UDP, 0
    //默认都是阻塞
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.9.3 bind函数

    本地地址信息关联到套接字上。

    int bind(int sockfd, const struct* addr, socklen_t addrlen);
    //成功0,失败-1,errno获取错误码
    
    • 1
    • 2
    sockaddr_in in;
    in_addr_t ip = inet_addr("192.168.13.25");
    if(ip != -1)
    	in.sin_addr.s_addr  = ip;
    	
    //#define INADDR_ANY ((in_addr_t) 0x00000000)
    in.sin_addr.s_addr = htonl(INADDR_ANY);
    
    //errno,98,端口占用、未释放或程序未正常结束
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.9.4 listen函数

    套接字处于监听状态。

    int listen(int sockfd, int backlog);
    //成功0,失败-1
    
    • 1
    • 2

    4.9.5 accept函数

    从监听套接字的客户连接请求队列获取客户端请求,并创建新的套接字来和客户端通信。

    int accept(int sockfd, struct sockaddr *addr, socklen_t * addrlen);
    //失败-1
    
    • 1
    • 2

    4.9.6 connect函数

    请求与监听套接字建立连接。

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    //成功0,失败-1
    
    • 1
    • 2

    非阻塞时,可设置连接超时时间,通过error中EINRPOCESS(Operation now in progress)判断。

    4.9.7 send函数

    发送数据,复制到套接字的发送缓冲区。

    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    //成功返回发送拷贝字节数,对方关闭返回0,错误-1
    
    • 1
    • 2

    TCP有发送缓冲区,UDP无发送缓冲区。

    非阻塞,可利用error变量EAGAIN

    send用于有连接的套接字。

    sendto和sendmsg用于有或无连接的套接字。

    4.9.8 recv函数

    接收数据.

    ssize_t recv(int sockfd, const void *buf, size_t len, int flags);
    //成功返回接收字节数,对方关闭返回0,错误-1,errno是EINTR、EWOULDBLOCK或EAGAIN时连接正常
    
    • 1
    • 2

    recvfrom也能接收数据。

    4.9.9 close函数

    关闭套接字

    #include 
    int close(int fd);
    //成功0,失败-1
    
    • 1
    • 2
    • 3

    4.10 简单的TCP套接字编程

    4.4.server.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
    	int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    
    	char on = 1;
    	setsockopt(sockSrv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    	struct sockaddr_in addrSrv;
    	memset(&addrSrv, 0, sizeof(struct sockaddr_in));
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_port = htons(8000);
    
    	if (-1 == bind(sockSrv, (struct sockaddr *)&addrSrv, sizeof(struct sockaddr)))
    	{
    		printf("bind() fail: %d!\n", errno);
    		return -1;
    	}
    
    	const int len = sizeof(struct sockaddr_in);
    
    	struct sockaddr_in serv;
    	getsockname(sockSrv, (struct sockaddr *)&serv, (socklen_t *)&len);
    	printf("server has started, ip=%s, port=%d\n", inet_ntoa(serv.sin_addr), ntohs(serv.sin_port));
    
    	listen(sockSrv, 5);
    
    	struct sockaddr_in addrClient;
    	while (1)
    	{
    		printf("--------wait for client-----------\n");
    		int sockConn = accept(sockSrv, (struct sockaddr *)&addrClient, (socklen_t *)&len);
    
    		char sendBuf[100] = {0};
    		sprintf(sendBuf, "Welcome client(%s: %d) to Server!", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));
    		// send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
    		send(sockConn, sendBuf, sizeof(sendBuf), 0);
    
    		char recvBuf[100];
    		recv(sockConn, recvBuf, 100, 0);
    		printf("Receive client's msg: %s\n", recvBuf);
    
    		close(sockConn);
    		/*
    		puts("continue to listen?(y/n)");
    		char ch[2];
    		scanf("%s", ch, 2);
    
    		if (ch[0] != 'y')
    			break;
    		*/
    	}
    
    	close(sockSrv);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    4.4.client.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
    	int sockClient = socket(AF_INET, SOCK_STREAM, 0);
    
    	struct sockaddr_in addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_port = htons(8000);
    	int err = connect(sockClient, (struct sockaddr *)&addrSrv, sizeof(struct sockaddr));
    	if (-1 == err)
    	{
    		printf("Failed to connect to the server.Please check whether the server is started\n");
    		return 0;
    	}
    
    	char recvBuf[100] = {0};
    	recv(sockClient, recvBuf, 100, 0);
    	printf("receive server's msg: %s\n", recvBuf);
    
    	char msg[100] = "hi,server";
    	// send(sockClient, msg, strlen(msg) + 1, 0);
    	send(sockClient, msg, sizeof(msg), 0);
    
    	close(sockClient);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    4.5.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUFFER_SIZE 512
    
    unsigned long GetTickCount()
    {
    	struct timeval tv;
    	if (gettimeofday(&tv, NULL) != 0)
    		return 0;
    
    	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
    }
    
    int main()
    {
    	struct sockaddr_in server_address;
    	memset(&server_address, 0, sizeof(server_address));
    	server_address.sin_family = AF_INET;
    	server_address.sin_addr.s_addr = inet_addr("192.168.0.88");
    	server_address.sin_port = htons(13334);
    
    	int sock = socket(PF_INET, SOCK_STREAM, 0);
    	assert(sock >= 0);
    
    	long t1 = GetTickCount();
    	int ret = connect(sock, (struct sockaddr *)&server_address, sizeof(server_address));
    	if (ret == -1)
    	{
    		long t2 = GetTickCount();
    		printf("connect() failed: %d\n", ret);
    		printf("time used: %ldms\n", t2 - t1);
    		if (errno == EINPROGRESS)
    			printf("unblock mode ret code...\n");
    	}
    	else
    		printf("ret code is: %d\n", ret);
    
    	close(sock);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    4.11 深入理解TCP编程

    4.11.1 数据发送和接收涉及的缓冲区

    应用缓冲区和TCP套接字缓冲区(内核缓冲区)。

    4.11.2 TCP数据传输的特点

    一、字节流,无消息边界。
    二、send后并不立即发送数据,内核控制。
    三、数据发送速度,网络状态决定。
    四、控制数据真实发送,网络状态决定。
    五、recv时并不知道真实已接收多少数据。

    4.11.3 数据发送的六种情形

    假设调用两次send,发送数据A和数据B。(send(A),send(B)),真实发送情况:
    一、网络情况良好,未受发送窗口、拥塞窗口和TCP最大传输单元影响,A、B变成两个数据段发送。
    二、网络不好,发送A被延迟,A、B数据合并,且长度未超过窗口大小和最大传输单元。AB合并发送一次。
    三、网络不好,发送A被延迟,A、B数据合并,且长度超过窗口大小或最大传输单元。AB合并发送(AB1、B2)。
    四、网络不好,发送A被延迟,A、B数据合并,且长度超过窗口大小或最大传输单元。AB合并发送(A1、A2B)。
    五、接收窗口小,AB分成多份发送。
    六、发送错误,失败。

    4.11.4 数据接收时的情形

    1. 接收到本次达到接收端的全部数据
    2. 接收到达到接收端的部分数据
    3. 没有收到数据

    send与实际发送次数无关,send与recv次数无关。

    4.11.5 一次请求响应的数据接收

    接收到全部数据后断开连接,通过recv返回0判断发送方数据发送完毕。

    4.6.server.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_LEN 300
    
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr SOCKADDR;
    
    int main()
    {
    	int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    	assert(sockSrv >= 0);
    
    	char on = 1;
    	setsockopt(sockSrv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_port = htons(8000);
    
    	bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	listen(sockSrv, 5);
    
    	const int len = sizeof(SOCKADDR);
    
    	SOCKADDR_IN addrClient;
    
    	while (1)
    	{
    		printf("--------wait for client-----------\n");
    
    		int sockConn = accept(sockSrv, (SOCKADDR *)&addrClient, (socklen_t *)&len);
    		char sendBuf[100];
    		for (int i = 0; i < 10; i++)
    		{
    			memset(sendBuf, 0, sizeof(sendBuf));
    			sprintf(sendBuf, "N0.%d Welcome to the server. What is 1 + 1 = ? (client IP: %s, client Port: %d)\n", i + 1, inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));
    			send(sockConn, sendBuf, sizeof(sendBuf), 0);
    		}
    
    		int iRes = shutdown(sockConn, SHUT_WR);
    		if (iRes == -1)
    		{
    			printf("shutdown failed with error: %d\n", errno);
    			close(sockConn);
    			return 1;
    		}
    
    		char recvBuf[BUF_LEN];
    		do
    		{
    			iRes = recv(sockConn, recvBuf, BUF_LEN, 0);
    			if (iRes > 0)
    			{
    				printf("Recv %d bytes.\n", iRes);
    				for (int i = 0; i < iRes; i++)
    					printf("%c", recvBuf[i]);
    				printf("\n");
    			}
    			else if (iRes == 0)
    			{
    				printf("The client has closed the connection.\n");
    			}
    			else
    			{
    				printf("recv failed with error: %d\n", errno);
    				close(sockConn);
    				return 1;
    			}
    
    		} while (iRes > 0);
    
    		close(sockConn);
    		/*
    		puts("Continue monitoring?(y/n)");
    		char ch[2];
    		scanf("%s", ch, 2);
    		if (ch[0] != 'y')
    			break;
    		*/
    	}
    
    	close(sockSrv);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    4.6.client.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_LEN 300
    
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr SOCKADDR;
    
    int main()
    {
    	int sockClient = socket(AF_INET, SOCK_STREAM, 0);
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_port = htons(8000);
    	int err = connect(sockClient, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	if (-1 == err)
    	{
    		printf("Failed to connect to the server. Please check whether the server is started\n");
    		return 0;
    	}
    
    	char recvBuf[BUF_LEN];
    	int iRes;
    	do
    	{
    		iRes = recv(sockClient, recvBuf, BUF_LEN, 0);
    		if (iRes > 0)
    		{
    			printf("\nRecv %d bytes:", iRes);
    			for (int i = 0; i < iRes; i++)
    				printf("%c", recvBuf[i]);
    			printf("\n");
    		}
    		else if (iRes == 0)
    		{
    			puts("The server has closed the send connection.\n");
    		}
    		else
    		{
    			printf("recv failed:%d\n", errno);
    			close(sockClient);
    			return 1;
    		}
    	} while (iRes > 0);
    
    	char sendBuf[100];
    	for (int i = 0; i < 10; i++)
    	{
    		memset(sendBuf, 0, sizeof(sendBuf));
    		sprintf(sendBuf, "N0.%d I'm the client, 1+1=2\n", i + 1);
    		send(sockClient, sendBuf, strlen(sendBuf), 0);
    	}
    	puts("Sending data to the server is completed.");
    
    	close(sockClient);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    4.11.6 多次请求响应的数据接收

    1. 定长数据的接收
    4.7.server.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_LEN 300
    
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr SOCKADDR;
    
    int main()
    {
    	int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    	assert(sockSrv >= 0);
    
    	char on = 1;
    	setsockopt(sockSrv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_port = htons(8000);
    
    	bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	listen(sockSrv, 5);
    
    	const int len = sizeof(SOCKADDR);
    
    	SOCKADDR_IN addrClient;
    	char sendBuf[111];
    	char recvBuf[BUF_LEN];
    
    	while (1)
    	{
    		printf("--------wait for client-----------\n");
    		int sockConn = accept(sockSrv, (SOCKADDR *)&addrClient, (socklen_t *)&len);
    		printf("--------client comes-----------\n");
    		memset(sendBuf, 'a', 111);
    		for (int cn = 0; cn < 50; cn++)
    		{
    			if (cn == 49)
    				sendBuf[110] = 'b';
    			send(sockConn, sendBuf, 111, 0);
    		}
    
    		int iRes;
    		do
    		{
    			iRes = recv(sockConn, recvBuf, BUF_LEN, 0);
    			if (iRes > 0)
    			{
    				printf("\nRecv %d bytes:", iRes);
    				for (int i = 0; i < iRes; i++)
    					printf("%c", recvBuf[i]);
    				printf("\n");
    			}
    			else if (iRes == 0)
    			{
    				printf("The client closes the connection.\n");
    			}
    			else
    			{
    				printf("recv failed with error: %d\n", errno);
    				close(sockConn);
    				// return 1;
    				continue;
    			}
    
    		} while (iRes > 0);
    
    		close(sockConn);
    		/*
    		puts("Continue monitoring?(y/n)");
    		char ch[2];
    		scanf("%s", ch, 2);
    		if (ch[0] != 'y')
    			break;
    		*/
    	}
    
    	close(sockSrv);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    4.7.client.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_LEN 250
    
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr SOCKADDR;
    
    int main()
    {
    	int sockClient = socket(AF_INET, SOCK_STREAM, 0);
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_port = htons(8000);
    	int err = connect(sockClient, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	if (-1 == err)
    	{
    		printf("Failed to connect to the server:%d\n", errno);
    		return -1;
    	}
    
    	char recvBuf[BUF_LEN];
    	int iRes;
    	int cn = 1;
    
    	for (int leftlen = 50 * 111; leftlen > 0; leftlen -= iRes)
    	{
    		iRes = recv(sockClient, recvBuf, min(BUF_LEN, leftlen), 0);
    		if (iRes > 0)
    		{
    			printf("\nNo.%d: Recv %d bytes: ", cn++, iRes);
    			for (int i = 0; i < iRes; i++)
    				printf("%c", recvBuf[i]);
    			printf("\n");
    		}
    		else if (iRes == 0)
    		{
    			puts("\nThe server has closed the send connection.\n");
    			break;
    		}
    		else
    		{
    			printf("recv failed: %d\n", errno);
    			close(sockClient);
    			return -1;
    		}
    	}
    
    	char sendBuf[100];
    	memset(sendBuf, 0, sizeof(sendBuf));
    	sprintf(sendBuf, "Hi, Server, I've finished receiving the data.");
    	send(sockClient, sendBuf, strlen(sendBuf), 0);
    
    	puts("Sending data to the server is completed");
    	close(sockClient);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    1. 变长数据的接收
      数据报末尾加结束标识符
      变长消息体前加固定长度的报头,报头内加入消息体长度字段
    struct MyData
    {
    	int nLen;
    	char data[0];
    };
    
    char str[256]="123456789";
    int nLen = strlen(str);
    struct MyData* p = (struct MyData*)malloc(sizeof(struct MyData)+nLen);
    memcpy(p->data, str, nLen);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    4.8.cpp
    #include 
    #include 
    #include 
    using namespace std;
    
    struct MyData
    {
    	int nLen;
    	char data[0];
    };
    
    int main()
    {
    	cout << "Size of MyData: " << sizeof(MyData) << endl;
    
    	char str[10] = "123456";
    	int nLen = sizeof(str);
    
    	MyData *myData = (MyData *)malloc(sizeof(MyData) + nLen);
    
    	myData->nLen = nLen;
    	memcpy(myData->data, str, nLen);
    
    	cout << "myData's Data is: " << myData->data << endl;
    	cout << "Size of MyData: " << sizeof(MyData) << endl;
    
    	free(myData);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    4.9.server.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_LEN 300
    
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr SOCKADDR;
    
    struct MyData
    {
    	int nLen;
    	char data[0];
    };
    
    int main()
    {
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_port = htons(8000);
    
    	int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    	bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	listen(sockSrv, 5);
    
    	SOCKADDR_IN addrClient;
    	int len = sizeof(SOCKADDR);
    	int cn = 5550;
    	struct MyData *mydata;
    	int iRes;
    	char recvBuf[BUF_LEN];
    	while (1)
    	{
    		printf("--------wait for client-----------\n");
    		int sockConn = accept(sockSrv, (SOCKADDR *)&addrClient, (socklen_t *)&len);
    		printf("--------client comes-----------\n");
    
    		mydata = (MyData *)malloc(sizeof(MyData) + cn);
    		mydata->nLen = htonl(cn);
    		memset(mydata->data, 'a', cn);
    		mydata->data[cn - 1] = 'b';
    		send(sockConn, (char *)mydata, sizeof(MyData) + cn, 0);
    		free(mydata);
    
    		do
    		{
    			iRes = recv(sockConn, recvBuf, BUF_LEN, 0);
    			if (iRes > 0)
    			{
    				printf("\nRecv %d bytes: ", iRes);
    				for (int i = 0; i < iRes; i++)
    					printf("%c", recvBuf[i]);
    				printf("\n");
    			}
    			else if (iRes == 0)
    			{
    				printf("\nThe client has closed the connection.\n");
    			}
    			else
    			{
    				printf("recv failed with error: %d\n", errno);
    				close(sockConn);
    				return 1;
    			}
    
    		} while (iRes > 0);
    
    		close(sockConn);
    		/*
    		puts("Continue monitoring?(y/n)");
    		char ch[2];
    		scanf("%s", ch, 2);
    		if (ch[0] != 'y')
    			break;
    		*/
    	}
    
    	close(sockSrv);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    4.9.client.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_LEN 250
    
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr SOCKADDR;
    
    int main()
    {
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_port = htons(8000);
    
    	int sockClient = socket(AF_INET, SOCK_STREAM, 0);
    	int err = connect(sockClient, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	if (-1 == err)
    	{
    		printf("Failed to connect to the server: %d\n", errno);
    		return -1;
    	}
    
    	int leftlen;
    	int iRes = recv(sockClient, (char *)&leftlen, sizeof(int), 0);
    	leftlen = ntohl(leftlen);
    	printf("Need to receive %d bytes data.\n", leftlen);
    
    	char recvBuf[BUF_LEN];
    	int cn = 1;
    
    	for (; leftlen > 0; leftlen -= iRes)
    	{
    		iRes = recv(sockClient, recvBuf, min(leftlen, BUF_LEN), 0);
    		if (iRes > 0)
    		{
    			printf("\nNo.%d: Recv %d bytes: ", cn++, iRes);
    			for (int i = 0; i < iRes; i++)
    				printf("%c", recvBuf[i]);
    			printf("\n");
    		}
    		else if (iRes == 0)
    		{
    			puts("\nThe server has closed the send connection.\n");
    			break;
    		}
    		else
    		{
    			printf("recv failed: %d\n", errno);
    			close(sockClient);
    			return -1;
    		}
    	}
    
    	char sendBuf[100];
    	memset(sendBuf, 0, sizeof(sendBuf));
    	sprintf(sendBuf, "I'm the client. I've finished receiving the data.");
    	send(sockClient, sendBuf, strlen(sendBuf), 0);
    
    	puts("Sending data to the server is completed");
    	close(sockClient);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    4.12 I/O控制命令

    设置套接字的工作模式(阻塞或非阻塞),获取套接字I/O操作的参数信息。

    #include 
    int ioctl(int fd, int request, ...);
    //成功0,失败-1,errno错误码
    
    • 1
    • 2
    • 3

    I/O控制命令request:

    • FIONBIO
      阻塞
    • FIONREAD
      流套接字,recv一次可读入数据量;数据报套接字,第一个数据报大小;缓冲区大小。
    • FIOASYNC
      异步
    int iMode = 0;
    ioctl(m_socket, FIONBIO, &iMode); //阻塞模式
    
    int num = 0;
    ioctl(0, FIONREAD, &iMode); //标准输入缓冲区字节数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    4.10.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    unsigned long GetTickCount()
    {
    	struct timeval tv;
    	if (gettimeofday(&tv, NULL) != 0)
    		return 0;
    	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
    }
    
    int main()
    {
    	char ip[] = "192.168.0.88";
    	in_addr_t dwIP = inet_addr(ip);
    	int port = 13334;
    
    	struct sockaddr_in server_address;
    	memset(&server_address, 0, sizeof(server_address));
    	server_address.sin_family = AF_INET;
    	server_address.sin_addr.s_addr = dwIP;
    	server_address.sin_port = htons(port);
    
    	int sock = socket(PF_INET, SOCK_STREAM, 0);
    	assert(sock >= 0);
    
    	long t1 = GetTickCount();
    	int ret = connect(sock, (struct sockaddr *)&server_address, sizeof(server_address));
    	printf("connect ret code is: %d\n", ret);
    	if (ret == -1)
    	{
    		long t2 = GetTickCount();
    		printf("time used: %ldms\n", t2 - t1);
    		printf("connect() failed...\n");
    		if (errno == EINPROGRESS)
    			printf("unblock mode ret code...\n");
    	}
    	else
    		printf("ret code is: %d\n", ret);
    
    	int argp = 1;
    	int res = ioctl(sock, FIONBIO, &argp);
    	if (-1 == res)
    	{
    		printf("Error at ioctlsocket(): %d\n", errno);
    		return -1;
    	}
    
    	puts("\nAfter setting non blocking mode:");
    
    	t1 = GetTickCount();
    	ret = connect(sock, (struct sockaddr *)&server_address, sizeof(server_address));
    	printf("connect ret code is: %d\n", ret);
    	if (ret == -1)
    	{
    		long t2 = GetTickCount();
    		printf("time used: %ldms\n", t2 - t1);
    		if (errno == EINPROGRESS)
    			printf("unblock mode errno: %d\n", errno);
    	}
    	else
    		printf("ret code is: %d\n", ret);
    
    	close(sock);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    4.13 套接字选项

    4.13.1 基本概念

    设置或获取套接字属性

    4.13.2 选项的级别

    适用范围或适用对象,有些选项针对特定协议,有些选项适用所有类型套接字。
    SO_TYPE
    SO_SNDBUF
    SO_REUSEADDR
    SO_RCVBUF
    SO_ERROR

    4.13.3 获取套接字选项

    #include 
    #include 
    int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
    //成功0,失败-1,errno错误码
    //EBADF:sockfd不是有效文件描述符
    //EFAULT:optlen太小或optval缓冲区非法
    //EINVAL:level未知或非法
    //ENOPROTOOPT:选项未知或不被指定协议族支持
    //ENOTSOCK:sockfd不是套接字描述符
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    4.11.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
    	int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    	if (s == -1)
    	{
    		printf("Error at socket()\n");
    		return -1;
    	}
    
    	int su = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    	if (s == -1)
    	{
    		printf("Error at socket()\n");
    		return -1;
    	}
    
    	int optVal;
    	int optLen = sizeof(optVal);
    	if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&optVal, (socklen_t *)&optLen) == -1)
    		printf("getsockopt failed: %d", errno);
    	else
    		printf("Size of stream socket receive buffer: %d bytes\n", optVal);
    
    	if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&optVal, (socklen_t *)&optLen) == -1)
    		printf("getsockopt failed: %d", errno);
    	else
    		printf("Size of streaming socket send buffer: %d bytes\n", optVal);
    
    	if (getsockopt(su, SOL_SOCKET, SO_RCVBUF, (char *)&optVal, (socklen_t *)&optLen) == -1)
    		printf("getsockopt failed: %d", errno);
    	else
    		printf("Size of datagram socket receive buffer: %d bytes\n", optVal);
    
    	if (getsockopt(su, SOL_SOCKET, SO_SNDBUF, (char *)&optVal, (socklen_t *)&optLen) == -1)
    		printf("getsockopt failed: %d", errno);
    	else
    		printf("Size of datagram socket send buffer: %d bytes\n", optVal);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    4.12.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
    	int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    	if (s == -1)
    	{
    		printf("Error at socket()\n");
    		return -1;
    	}
    	
    	int optVal;
    	int optLen = sizeof(optVal);
    	if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&optVal, (socklen_t *)&optLen) == -1)
    		printf("getsockopt failed: %d", errno);
    	else
    	{
    		if (SOCK_STREAM == optVal)
    			printf("The current socket is a stream socket.\n");
    		else if (SOCK_DGRAM == optVal)
    			printf("The current socket is a datagram socket.\n");
    	}
    
    	int su = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    	if (su == -1)
    	{
    		printf("Error at socket()\n");
    		return -1;
    	}
    	if (getsockopt(su, SOL_SOCKET, SO_TYPE, (char *)&optVal, (socklen_t *)&optLen) == -1)
    		printf("getsockopt failed: %d", errno);
    	else
    	{
    		if (SOCK_STREAM == optVal)
    			printf("The current socket is a stream socket.\n");
    		else if (SOCK_DGRAM == optVal)
    			printf("The current socket is a datagram socket.\n");
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    4.13.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    typedef struct sockaddr SOCKADDR;
    
    int main()
    {
    	int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    	if (s == -1)
    	{
    		printf("Error at socket()\n");
    		return -1;
    	}
    
    	char on = 1;
    	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    	char ip[] = "127.0.0.1";
    	struct sockaddr_in service;
    	service.sin_family = AF_INET;
    	service.sin_addr.s_addr = inet_addr(ip);
    	service.sin_port = htons(8000);
    	if (bind(s, (SOCKADDR *)&service, sizeof(service)) == -1)
    	{
    		printf("bind failed: %d\n", errno);
    		return -1;
    	}
    
    	int optVal;
    	int optLen = sizeof(optVal);
    	if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, (char *)&optVal, (socklen_t *)&optLen) == -1)
    		printf("getsockopt failed: %d", errno);
    	else
    		printf("Before listening, The value of SO_ACCEPTCONN: %d, The socket is not listening\n", optVal);
    
    	if (listen(s, 100) == -1)
    	{
    		printf("listen failed: %d\n", errno);
    		return -1;
    	}
    
    	if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, (char *)&optVal, (socklen_t *)&optLen) == -1)
    	{
    		printf("getsockopt failed: %d", errno);
    		return -1;
    	}
    	else
    		printf("After listening, The value of SO_ACCEPTCONN: %d, The socket is listening\n", optVal);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    4.13.4 设置套接字选项

    #include 
    #include 
    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t *optlen);
    //成功0,失败-1,errno错误码
    
    • 1
    • 2
    • 3
    • 4
    4.14.c
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    typedef struct sockaddr SOCKADDR;
    
    int main()
    {
    	int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    	if (s == -1)
    	{
    		printf("Error at socket()\n");
    		return -1;
    	}
    
    	char on = 1;
    	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    	char ip[] = "127.0.0.1";
    	struct sockaddr_in service;
    	service.sin_family = AF_INET;
    	service.sin_addr.s_addr = inet_addr(ip);
    	service.sin_port = htons(9900);
    	if (bind(s, (SOCKADDR *)&service, sizeof(service)) == -1)
    	{
    		printf("bind failed\n");
    		return -1;
    	}
    
    	int optVal = 1;
    	int optLen = sizeof(int);
    	if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&optVal, (socklen_t *)&optLen) == -1)
    	{
    		printf("getsockopt failed: %d", errno);
    		return -1;
    	}
    	else
    		printf("After bind, the value of SO_KEEPALIVE: %d\n", optVal);
    
    	optVal = 1;
    	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&optVal, optLen) != -1)
    		printf("Successful activation of keep alive mechanism.\n");
    
    	if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&optVal, (socklen_t *)&optLen) == -1)
    	{
    		printf("getsockopt failed: %d", errno);
    		return -1;
    	}
    	else
    		printf("After setting, the value of SO_KEEPALIVE: %d\n", optVal);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
  • 相关阅读:
    redis的优势和缺点要注意的
    leetcode 20. 有效的括号
    王怀远:阿里云一站式物联网存储架构设计
    net-java-php-python-单位办公OA系统计算机毕业设计程序
    体系结构30_同步性能问题
    java ThreadLocal
    【java】异常
    牛客网_HJ1_字符串最后一个单词的长度
    引导流程分析——BIOS 与 UEFI
    latex bib title变小写了怎么办
  • 原文地址:https://blog.csdn.net/oqqyx1234567/article/details/127484415