网络编程--接口函数

1.socket函数
为了执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型
#include <sys/socket.h>
int socket (int family, int type, int protocol);
//返回:若成功则为非负描述符,若出错则为-1
其中family指明协议族,type参数指明套接字类型,protocol参数应该设为某个(见下图)协议类型常值,或者设为0,以选择所给定family和type组合的系统默认值
 
socket函数的family常值
family 说 明
AF_INET
AF_INET6
AF_LOCAL
AF_ROUTE
AF_KEY
IPv4协议
IPv6协议
Unix域协议
路由套接口
密钥套接口
socket函数的type常值
type 说 明
SOCK_STREAM
SOCK_DGRAM
SOCK_SEQPACKET
SOCK_RAW
字节流套接口
数据报套接口
有序分组套接口
原始套接口
 socket函数的protocol常值
protocol 说 明
IPPROTO_TCP
IPPROTO_UDP
IPPROTO_SCTP
TCP传输协议
UDP传输协议
SCTP传输协议
socket函数中family和type参数的组合
2.connect函数
TCP客户用connect函数来建立与TCP服务器的连接
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
 //返回:若成功则为0,若出错则为-1
sockfd是socket函数返回的套接字描述符,剩下的2个参数分别是一个指向套接字地址结构的指针和该结构的大小。connect函数将激发TCP的三次握手过程,而且仅在连接建立成功或出错时才返回,其中出错有如下几种情况:
1).若TCP客户没有收到SYN包的响应,则返回ETIMEDOUT错误。如调用该函数时,内核发送一个SYN,若无响应则等待6s后再发一个,若仍无响应,则等待24s再发一个,若总共等了75s后仍未收到响应消息则返回该错误(因内核而异)。
2).若响应时RST,表明该服务器主机在我们指定的端口上没有进程等待,客户收到RST包后马上返回ECONNREFUSED错误。
3).若客户发出的SYN在中间的路由器上引发了一个“destination unreachable”的ICMP错误,则按第一种情形继续发送SYN,若在规定的时间内没有收到回应,则将ICMP错误作为EHOSTUNREACH或ENETUNREACH错误返回。
 
3.bind函数
bind函数把一个本地协议地址赋予一个套接字。对于网际协议,协议地址是一个ip地址和一个端口号
#include <sys/socket.h>
int bind (int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
//返回,成功为0,出错为-1
参数sockfd是socket函数返回的套接字描述符,myaddr是一个指向特定于协议的地址结构的指针,第三个参数是该地址结构的长度,对于TCP,调用bind函数可以指定一个端口,或者指定一个地址,也可以两者都指定,还可以都不指定:
  • 服务器在启动时候捆绑他们众所周知的端口
  • 进程可以把一个特定的IP地址捆绑到它的套接字上,不过这个IP地址必须属于其所在主机的网络接口之一
     其中对于IPv4来说,通配地址常值INADDR_ANY来指定,其值一般为0,它通知内核选择IP地址
4.listen函数
函数listen 仅被TCP服务器调用,它做两件事件:
1).当函数socket创建一个套接口时,它被假设为一个主动套接口,也就是说,它是一个将调用connect发起连接的客户套接口,函数listen将未连接的套接口转换成被动套接口,指示内核应接受指向此套接口的连接请求,
2).函数的第二个参数规定了内核为此套接口排队的最大连接个数
#include <sys/socket.h> 
int listen (int sockfd, int backlog);
//返回,成功为0,出错-1 
要理解backlog参数,我们要知道内核为任何一个给定的监听套接字维护2个队列:
1).未完成连接队列。客户和服务器之间的tcp三次握手并未完成。
2).已完成连接队列。tcp的三次握手已经完成,处于ESTABLISHED状态。

关于两个队列的处理:
  • listen函数的backlog参数曾被规定为两个队列总和的最大值
  • 源自Berkeley的实现给backlog增设了一个模糊因子,把它乘以1.5得到未处理队列最大长度
  • 不要把backlog定义为0,因为不同的实现对此有不同的解释
  • 在三路握手正常完成的前提下(也就是说没有丢失分节,从而没有重传),未完成连接队列的任何一项在其中的存留时间就是一个RTT,而RTT的值取决于特定的客户与服务器
  • 当一个客户SYN到达时,若这些队列是满的,TCP就忽略该分节,也就是不发送RST
  • 在三路握手完成后,但在服务器调用accept之前到达的数据应由服务器TCP排列,最大数据量为相应已连接套接字的接受缓冲区大小
5.accept函数
accept函数由TCP服务器调用,用于从已完成连接队列列头返回下一个已完成连接,如果已完成连接队列为空,进程将被投入睡眠(如果套接字为默认的阻塞方式)
#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
//返回:若成功为非负描述符,出错为-1
参数cliaddr和addrlen返回已连接的客户的协议地址,如果对客户的协议地址不感兴趣,可以置为空,参数addrlen在函数调用的时候是传入的套接字地址结构的大小,函数返回时它的值是内核存放在该套接字地址结构中的确切字节数。
如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与返回客户的TCP连接,一般我们称accept函数第一个参数为监听套接字描述符(由socket创建,随后用作bind和listen的第一个参数的描述符),称它的返回值为已连接套接字描述符
accept 函数最多返回三个值:一个既可能是新的套接字描述符也可能是出错指示的整数、客户进程的协议地址(由cliaddr指针所指)、以及该地址的大小(由addrlen指针所指)。
 
TCP cs模型

server.c

#include <sys/types.h>      
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>

#define SERVER_PORT 5555 
#define MAXLINE 4096
int main(void)
{
    struct sockaddr_in serveraddr, clientaddr;
    int sockfd, addrlen, confd, len, i;
    char ipstr[128];
    char buf[MAXLINE];

    //1.socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    //2.bind
    bzero(&serveraddr, sizeof(serveraddr));
    /* 地址族协议IPv4 */
    serveraddr.sin_family = AF_INET;
    /* IP地址 */
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(SERVER_PORT);
    bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    //3.listen
    listen(sockfd, 128);
    while (1) {
        //4.accept阻塞监听客户端链接请求
        addrlen = sizeof(clientaddr);
        confd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen);
        //输出客户端IP地址和端口号
        printf("client ip %s	port %d
", 
                inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, ipstr, sizeof(ipstr)),
                ntohs(clientaddr.sin_port));

        //和客户端交互数据操作confd
        //5.处理客户端请求 
        len = read(confd, buf, sizeof(buf));
        i = 0;
        while (i < len) {
            buf[i] = toupper(buf[i]);
            i++;
        }
        write(confd, buf, len);

        close(confd);
    }
    close(sockfd);

    return 0;
}

client.c

#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define SERVER_PORT 8000
#define MAXLINE 4096
int main(int argc, char *argv[])
{
    struct sockaddr_in serveraddr;
    int confd, len;
    char ipstr[] = "192.168.6.254";
    char buf[MAXLINE];
    if (argc < 2) {
        printf("./client str
");
        exit(1);
    }
    //1.创建一个socket
    confd = socket(AF_INET, SOCK_STREAM, 0);
    //2.初始化服务器地址
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    //"192.168.6.254"
    inet_pton(AF_INET, ipstr, &serveraddr.sin_addr.s_addr);
    serveraddr.sin_port  = htons(SERVER_PORT);
    //3.链接服务器
    connect(confd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    //4.请求服务器处理数据
    write(confd, argv[1], strlen(argv[1]));
    len = read(confd, buf, sizeof(buf));
    write(STDOUT_FILENO, buf, len);

    //5.关闭socket
    close(confd);
    return 0;
}

Makefile

all:server client


server:server.c
    gcc $< -o $@

client:client.c
    gcc $< -o $@


.PHONY:clean
clean:
    rm -f server
    rm -f client

6.数据报接受与发送

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen); 
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags, const struct sockaddr *to, socklen_t addrlen);
//均返回:若成功则为读或写的字节数,出错为-1
默认情况recvfrom函数没有接收到对方数据时候是阻塞的

UDP cs模型

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <ctype.h>

#define SERVER_PORT 5556 
#define MAXLINE 1024 
int main(void)
{
    int sockfd,i;
    ssize_t len;
    struct sockaddr_in serveraddr,clientaddr;
    char buf[MAXLINE];
    char ipstr[INET_ADDRSTRLEN];//16个字节
    socklen_t clientlen;

    //构造用于UDP通信的套接字
    sockfd=socket(AF_INET,SOCK_DGRAM,0);

    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family =AF_INET;
    serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
    serveraddr.sin_port=htons(SERVER_PORT);
    //printf("%x
",INADDR_ANY);
    bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    while(1){    
        clientlen=sizeof(clientaddr);
        len=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&clientaddr,&clientlen);

        printf("client IP %s	 PORT %d
",
                inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),
                ntohs(clientaddr.sin_port));
        i=0;
        while(i<len){
            buf[i]=toupper(buf[i]);
            i++;
        }
        buf[i]='';
        sendto(sockfd,buf,len,0,(struct sockaddr*)&clientaddr,sizeof(clientaddr));
    }
    close(sockfd);
    return 0;
}

client.c

#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#define SERVER_PORT 5556 
#define MAXLINE 4096

int main(int argc,char *argv[])
{
    struct sockaddr_in serveraddr;
    int confd;
    ssize_t len;
    char ipstr[]="123.206.59.138";
    char buf[MAXLINE];
    if(argc <2){
        printf("./client str
");
        exit(1);
    }
    //1.创建一个socket
    confd=socket(AF_INET,SOCK_DGRAM,0);
    //2.初始化服务器地址
    bzero(&serveraddr,sizeof(serveraddr));
    serveraddr.sin_family=AF_INET;
    //
    inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);
    serveraddr.sin_port =htons(SERVER_PORT);
    //3向务器发送数据
    sendto(confd,argv[1],strlen(argv[1]),0,(struct sockaddr *)&serveraddr,sizeof(serveraddr));

    len=recvfrom(confd,buf,sizeof(buf),0,NULL,0);
    write(STDIN_FILENO,buf,len);
    close(confd);
    return 0;
}    
原文地址:https://www.cnblogs.com/tla001/p/6603027.html