TCP编程

1 SOCKET

2 IP地址

IPV4地址:
点分形式: 192.168.7.246
32位整数

特殊IP地址:

局域网IP: 192.XXX.XXX.XXX  10.XXX.XXX.XXX
广播IP: xxx.xxx.xxx.255, 255.255.255.255(全网广播)
组播IP: 224.XXX.XXX.XXX~239.xxx.xxx.xxx

3 端口号

16位的数字(1-65535)

众所周知端口: 1~1023(FTP: 21,SSH: 22, HTTP:80, HTTPS:443)

保留端口: 1024-5000(不建议使用)

可以使用的:5000~65535

TCP端口和UDP端口是相互独立的

网络里面的通信是由 IP地址+端口号 来决定

4 字节序

字节序是指不同的CPU访问内存中的多字节数据时候,存在大小端问题

如CPU访问的是字符串,则不存在大小端问题

一般来说:

X86/ARM: 小端
powerpc/mips, ARM作为路由器时,大端模式

网络传输的时候采用大端模式

本地字节序、网络字节序

IP地址转换函数:

 in_addr_t inet_addr(const char *cp);

cp: 点分形式的IP地址,结果是32位整数(内部包含了字节序的转换,默认是网络字节序的模式)
特点: 1. 仅适应于IPV4
2. 当出错时,返回-1
3.此函数不能用于255.255.255.255的转换

 inet_pton()/inet_ntop()

 #include <arpa/inet.h>

 int inet_pton(int af,const char* src,void* dst);
 const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

特点: 1.适应于IPV4和IPV6
2.能正确的处理255.255.255.255的转换问题
参数:
1. af: 地址协议族(AF_INET或AF_INET6)
2. src:是一个指针(填写点分形式的IP地址[主要指IPV4])
3. dst: 转换的结果给到dst

RETURN VALUE
inet_pton() returns 1 on success (network address was successfully converted). 0 is returned if src
does not contain a character string representing a valid network address in the specified address family.
If af does not contain a valid address family, -1 is returned and errno is set to EAFNOSUPPORT.

TCP编程API

1.socket()函数

 #include <sys/types.h>          /* See NOTES */
 #include <sys/socket.h>

 int socket(int domain, int type, int protocol);

1.1 参数:

1.domain:

    AF_INET             IPv4 Internet protocols          ip(7)
    AF_INET6            IPv6 Internet protocols          ipv6(7)
    AF_UNIX, AF_LOCAL   Local communication              unix(7)
    AF_NETLINK          Kernel user interface device     netlink(7)
    AF_PACKET           Low level packet interface       packet(7)

2.type:

   SOCK_STREAM: 流式套接字 唯一对应于TCP
   SOCK_DGRAM: 数据报套接字,唯一对应着UDP
   SOCK_RAW: 原始套接字

3.protocol: 一般填0,原始套接字编程时需填充

1.2 返回值:

RETURN VALUE
On success, a file descriptor for the new socket is returned.
On error, -1 is returned, and errno is set appropriately.

成功时返回文件描述符,出错时返回为-1

1.3 示例代码

    int fd = -1,fd_client;
    int result = 0;
    struct sockaddr_in mysockaddr;
    struct sockaddr_in clientscokaddr;
    socklen_t clientaddrlen;
    short int SERV_PORT = 5050;
    char botton[64];

    /* 1. 创建socket fd */
    fd = socket(AF_INET,SOCK_STREAM,0);
    if (fd < 0) {
        perror("socket");
        return -1;
    }

2.bind()函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

2.1 参数:

sockfd: 通过socket()函数拿到的fd
addr: struct sockaddr的结构体变量的地址
addrlen: 地址长度

2.2 返回值

RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

2.3 示例代码:

    /*2. 绑定 */
    /*2.1 填充struct sockaddr_in结构体变量 */
    bzero((void*)&mysockaddr,sizeof(mysockaddr));
    mysockaddr.sin_family = AF_INET;
    mysockaddr.sin_port = htons(SERV_PORT);//网络字节序的端口号
#if 1    
    //mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
    mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
#else
    result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr);
    if (result != 1) {
        perror("inet_pton");
        return -1;
    }
#endif
    /*2.2 绑定 */
    result = bind(fd,(struct sockaddr*)&mysockaddr,sizeof(mysockaddr));
    if (result != 0) {
        perror("bind");
        return -1;
    }

如果是IPV6的编程,要使用struct sockddr_in6结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程

3 listen()函数: 把主动套接字变成被动套接字

    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>

    int listen(int sockfd, int backlog);

3.1 参数

    sockfd: 通过socket()函数拿到的fd
    backlog: 同时允许几路客户端和服务器进行正在连接的过程(正在三次握手)
    一般填5, 测试得知,ARM最大为8

    内核中服务器的套接字fd会维护2个链表:
        1.正在三次握手的的客户端链表(数量=2*backlog+1)
        2.已经建立好连接的客户端链表(已经完成3次握手分配好了newfd)

    比如:listen(fd, 5); //表示系统允许11(=2*5+1)个客户端同时进行三次握手

3.2 返回值

RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

3.3 示例代码:

/*3. 调用listen()把主动套接字变成被动套接字 */
    result = listen(fd,5);
    if (result != 0) {
        perror("listen");
        return -1;
    }

4.accept()函数: 阻塞等待客户端连接请求

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <sys/socket.h>

       int accept4(int sockfd, struct sockaddr *addr,
                   socklen_t *addrlen, int flags);

4.1 参数

     sockfd: 经过前面socket()创建并通过bind(),listen()设置过的fd
         addr和addrlen: 获取连接过来的客户的信息

4.2 返回值

RETURN VALUE
On success, these system calls return a nonnegative integer that is a descriptor for the accepted socket.
On error, -1 is returned, and errno is set appropriately.

   成功时返回已经建立好连接的新的newfd

4.3 示例代码:

/*4. 阻塞等待客户端连接请求 */
#if 0
    bzero((void*)&clientscokaddr, sizeof(clientscokaddr)); 
    fd_client = accept(fd,NULL,NULL);
    if (-1 == result) {
        perror("accpet");
        return 0;
    }
#else
    /*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号*/
    bzero((void*)&clientscokaddr, sizeof(clientscokaddr));
    fd_client = accept(fd, (struct sockaddr*)&clientscokaddr, &clientaddrlen);
    if (-1 == result) {
        perror("accpet");
        return 0;
    }
    char ipv4_addr[16];
    if (!inet_ntop(AF_INET, (void*)&clientscokaddr.sin_addr, ipv4_addr, clientaddrlen)) {
        perror("inet_ntop");
        return -1;
    }

    printf("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(clientscokaddr.sin_port));

#endif

5. connect()函数:客户端的连接函数

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

5.1 参数

   sockfd: 通过socket()函数拿到的fd
   addr: struct sockaddr的结构体变量的地址 
   addrlen: 地址长度  

5.2 返回值

RETURN VALUE
If the connection or binding succeeds, zero is returned. On error, -1
is returned, and errno is set appropriately.

5.3 示例代码:

    result = connect(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr));
    if (result != 0) {
        perror("connect");
        return -1;
    }