网络编程--接口函数

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\tport %d\n",
                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\n");
        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 [email protected]

client:client.c
    gcc $< -o [email protected]

.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\n",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\t PORT %d\n",
                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]=‘\0‘;
        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\n");
        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;
}    
时间: 2024-10-16 11:13:55

网络编程--接口函数的相关文章

linux 网络编程常用函数及流程

一.网络编程之TCP流程 服务端:socket---bind---listen---while(1){---accept---recv---send---close---}---close 客户端:socket----------------------------------connect---send---recv-----------------close 二.网络编程常用函数 服务器端: 头文件包含: #include<sys/types.h> #include<sys/sock

linux socket网络编程 常用函数及头文件

转自:http://blog.chinaunix.net/u3/102500/showart_2065640.html 一 三种类型的套接字: 1.流式套接字(SOCKET_STREAM) 提供面向连接的可靠的数据传输服务.数据被看作是字节流,无长度限制.例如FTP协议就采用这种. 2.数据报式套接字(SOCKET_DGRAM) 提供无连接的数据传输服务,不保证可靠性. 3.原始式套接字(SOCKET_RAW) 该接口允许对较低层次协议,如IP,ICMP直接访问. 二 基本套接字系统调有有如下一

day8 网络编程 接口开发 异常处理

一 .在day7中已经讲了单个接口的开发,今天讲有关系的接口开发,也就是依赖性,比如你要在博客园写文章就必须先登录,登陆之后才能发表文章,那么登陆和发表文章之间就有个依赖关系,要做的就是这种开发: 1.接口开发首先要导入flask,导入我们用的着的方法 截图:__name__这里有必要解释一下,name前后是两个_不要写错 导入方法: 1.如果不嫌麻烦完全可以一级一级目录点出来, 2.也可以手动添加环境变量 3.就是可以用pycharm自动帮我们添加环境变量,右键选择Mark Directory

Linux网络编程-readn函数实现

readn函数功能:在网络编程的读取数据中,通常会需要用到一个读指定字节才返回的函数,linux系统调用中没有给出,需要自己封装. readn实现代码: int readn(int fd, void *vptr, size_t n) { size_t nleft = n; //readn函数还需要读的字节数 ssize_t nread = 0; //read函数读到的字节数 unsigned char *ptr = (char *)vptr; //指向缓冲区的指针 while (nleft >

四、网络编程-读写函数

1.写函数write 函数原型: size_t write(int fd,const void *buf,size_t nbytes); write函数将buf中的nbytes字节内容写入文件描述符fd,成功时返回写的字节数,失败时返回-1.在网络程序中,向套接字文件描述符写时有两种情况: 1)write的返回值大于0,表示写了部分或者是全部数据. 2)返回值小于0,此时出现了错误,要根据错误类型来处理.如果错误为EINTR,表示写的时候出现了中断错误:如果为EPIPE表示网络连接出现了问题(对

Linux网络编程--IO函数以及示例

网络数据能够正确到达用户并被用户接收是进行网络数据传输的基本目的, 网络数据的接受和发送有很多种方案,例如:直接发送和接收,通过向量发送和接收,使用消息发送和接收等.本篇文章主要介绍常用的IO函数以及用法,如:最常用的read()/write()函数,和其他标准的套接字专用函数recv()/send(),readv()/writev(),recvmsg()/sendmsg(). 各个函数原型以及介绍如下: ssize_t read(int fd, void *buf, size_t count)

linux c网络编程之函数

1. struct protoent *protocol=getprotobyname(char *p); 功能:通过协议名获取协议类型信息 解释:p为字符串指针,指向一个协议名,如icmp,struct protoent及函数在<netdb.h>中定义,protocol->p_proto为协议类型值. 2.

Unix网络编程 gethostbyname函数

gethostbyname函数 : 是将主机名转换成对应的IPv4地址: 包含头文件 #include <netdb.h> 函数原型: struct hostent *gethostbyname(const char *hostname) //返回: 成功返回一个非空指针,否则返回NULL并设置errno的值 返回: >>>   调用成功,返回一个指向hostent结构的指针,该结构含有所查找主机的所有IPv4地址: >>>   调用失败,返回空指针NULL

Linux网络编程---htons函数的使用

htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处. htonl就是把本机字节顺序转化为网络字节顺序所谓网络字节顺序(大尾顺序)就是指一个数在内存中存储的时候"高对低,低对高"(即一个数的高位字节存放于低地址单元,低位字节存放在高地址单元中).但是计算机的内存存储数据时有可能是大尾顺序或者小尾顺序.先举个例子:int a = 0x403214;int b = htonl(a);我在VC++6.0调试这段代码,发现&