网络编程基础

服务器端:

第一步,调用socket函数创建套接字

第二步,调用bind函数分配IP地址和端口号

第三步,调用listen函数转为可接收请求状态

第四步,调用accept函数受理连接请求

#include<sys/socket.h>

int socket(int domain, int type, int protocol);//成功时返回文件描述符,失败返回-1


一、domain: 套接字中使用的协议族信息

PF_INET: IPv4互联网协议族

PF_INET6: IPv6互联网协议族

PF_LOCAL: 本地通信的UNIX协议族

PF_PACKET: 底层套接字的协议族

PF_IPX: IPX Novell协议族

另外,套接字中实际采用的最终协议信息是通过socket函数中的第三个参数传递的。在指定的协议族范围内通过第一个参数决定第三个参数。

二、套接字类型

(1)面向连接的套接字(SOCK_STREAM)

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

2.按序传输数据

3.传输的数据不存在数据边界(read和write的调用次数并无太大意义)

注意:套接字连接必须一一对应。可靠的、按序传递的、基于字节的面向连接的数据传输方式的套接字。

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

1.强调快速传输而非传输顺序

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

3.传输的数据有数据边界(接收数据的次数和传输次数相同)

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

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


TCP套接字的数据收发无边界,服务器端即使调用一次write函数传输40字节的数据,客户端也有可能通过4次read函数调用每次读取10字节。那么读取10字节后,剩下的30字节在哪等候呢?事实上,write函数调用后并非立即传输数据,read函数调用后也并非马上接收数据。write函数调用瞬间,数据将移至输出缓冲;read函数调用瞬间,从输入缓冲读取数据。

缓冲特性:

1. I/O缓冲在每个TCP套接字中单独存在

2. I/O缓冲在创建套接字时自动生成

3. 即使关闭套接字也会继续传递输出缓冲中遗留的数据

4. 关闭套接字将丢失输入缓冲中的数据

write函数在数据移到输出缓冲时返回。TCP保证对输出缓冲数据的传输。

流控制是区分UDP和TCP的最重要的标志。

UDP套接字:只需一个UDP套接字就能和多台主机通信。

UDP服务器端/客户端不想TCP那样在连接状态下交换数据,因此与TCP不同,无需经过连接过程。也就是说,不必调用TCP连接过程中调用的listen函数和accept函数。UDP中只有创建套接字的过程和数据交换过程。

        

将初始化的地址信息分配给套接字:

#include<sys/socket.h>

int bind(int sockfd, struct sockaddr* myaddr, socklen_t addrlen);//成功返回0,失败返回-1

如果此函数调用成功,则将第二个参数指定的地址信息分配给第一个参数中的相应套接字。


struct sockaddr_in

{

        sa_family_t             sin_family;          //地址族

        uint16_t                  sin_port;             //16位TCP/UDP端口号

        struct in_addr         sin_addr;            //32位IP地址

        char                        sin_zero[8];        //不使用

}

struct in_addr 

{

        in_addr_t                s_addr;        //32位IPv4地址

}

一、sa_family_t地址族:

AF_INET: IPv4网络协议中使用的地址族

AF_INET6: IPv6网络协议中使用的地址族

AF_LOCAL: 本地通信中采用的UNIX协议的地址族


#include<sys/socket.h>

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


backlog:连接请求等待队列的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列。


调用listen函数后,若有新的连接请求,则应按序受理:

#include<sys/socket.h>

int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);成功时返回文件描述符,失败返回-1


addr:保存发起连接请求的客户端地址信息的变量地址值,调用函数后向传递来的地址变量参数填充客户端地址信息。

addrlen:第二个参数addr结构体的长度,但是存有长度的变量地址。函数调用完成后,该变量即被填入客户端地址长度。

客户端:

第一步,调用socket函数创建套接字

第二步,调用connect函数向服务器端发送连接请求

#include<sys/socket.h>

int connect(int sock , struct sockaddr* servaddr , socklen_t addrlen);//成功返回0,失败返回-1


sock:客户端套接字文件描述符

servaddr:保存目标服务器端地址信息的变量地址值

addrlen:以字节为单位传递已传递给第二个结构体参数servaddr的地址变量长度

客户端的IP地址和端口在调用connect函数时自动分配,无需调用标记的bind函数进行分配。

connect函数其实是服务器端把连接请求信息记录到等待队列,因此connect函数返回后并不立即进行数据交换。


将字符串形式的IP地址转换成32位整数型数据:

#include <arpa/inet.h>

in_addr_t inet_addr(const char* string);//成功返回32位大端序整数型值,失败返回INADDR_NONE。

inet_aton函数与inet_addr函数在功能上完全相同,也将字符串形式IP地址转换为32位网络字节序整数并返回。只不过该函数利用了in_addr结构体,且其使用频率更高。

#include<arpa/inet.h>

int inet_aton(const char* string , struct in_addr* addr);//成功返回1,失败返回0;

//string:含有需转换的IP地址信息的字符串地址值

//addr:将保存转换结果的in_addr结构体变量的地址值

inet_ntoa函数正好与inet_aton函数相反。此函数可以把网络字节序整数型IP地址转换成我们熟悉的字符串形式。

#include<arpa/inet.h>

char* inet_ntoa(struct in_addr adr);//成功返回转换的字符串地址值,失败返回-1

struct sockaddr_in addr;

char* serv_port = "9190";

memset(&addr,0,sizeof(addr));

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = htonl(INADDR_ANY);

addr.sin_port = htons(atoi(serv_port));

memset函数将每个字节初始化为同一值:第一个参数为结构体变量addr的地址值,即初始化对象为addr;第二个参数为0,因此初始化为0;最后一个参数中传入addr的长度,因此addr的所有字节均初始化为0。

atoi函数把字符串类型的值转换为整数型。

利用常数INADDR_ANY分配服务器端的IP地址,可以自动获取运行服务器端的计算机IP地址。

基于UDP的数据I/O函数

发送:

#include<sys/socket.h>

ssizez_t sendto(int sock, void* buff, size_t nbytes, int flags, struct sockaddr* to, socklen_t addrlen);//成功返回传输字节数,失败返回-1。

//sock:用于传输数据的UDP套接字文件描述符

//buff:保存待传输数据的缓冲地址值

//nbytes:待传输的数据长度,以字节为单位

//flags:可选项参数,若没有则传递0

//to:存有目标地址信息的sockaddr结构体变量的地址值

//addrlen:传递给参数to的地址值结构体变量的长度

接收:

#include<sys/socket.h>

ssizez_t recvfrom(int sock, void* buff, size_t nbytes, int flags, struct sockaddr* from, socklen_t* addrlen);//成功返回传输字节数,失败返回-1。

//sock:用于接收数据的UDP套接字文件描述符

//buff:保存接收数据的缓冲地址值

//nbytes:可接收的最大字节数,故无法超过参数buff所指的缓冲大小

//flags:可选项参数,若没有则传递0

//from:存有发送端地址信息的sockaddr结构体变量的地址值

//addrlen:保存参数from的结构体变量长度的变量地址值

UDP程序中,调用sendto函数传输数据前应完成对套接字的地址分配工作,因此调用bind函数。即bind函数不分TCP和UDP。另外,如果调用sendto函数时发现尚未分配地址信息,则在首次调用sendto函数时给相应套接字自动分配IP和端口。而且此时分配的地址一直保留到程序结束为止,因此也可用来与其他UDP套接字进行数据交换。综上所述,调用sendto函数时,自动分配IP和端口号,IP用主机IP,端口号选尚未使用的任意端口号。因此,UDP客户端通常无需额外的地址分配过程。

2两台主机建立了套接字连接,每个主机就会拥有单独的输入流和输出流。close函数将同时断开这两个流。shutdown函数用来关闭其中一个流:

#include<sys/socket.h>

int shutdown(int sock , int howto);//成功返回0,失败返回-1

//sock:需要断开的套接字文件描述符

//howto:传递断开方式信息

//SHUT_RD:断开输入流(套接字无法接收数据,即使输入缓冲收到数据也会抹去,而且无法调用输入相关函数)

//SHUT_WR:断开输出流(无法传输数据,但如果输出缓冲中还有未传输的数据,则将传递至目标主机)

//SHUT_RDWR:同时断开I/O流

半关闭的作用:向客户端传递EOF表示文件传输结束,防止客户端无休止调用输入函数导致的程序阻塞。

利用域名获取IP地址:通过传递字符串格式的域名获取IP地址

#include<netdb.h>

struct hostent* gethostbyname(const char* hostname);//成功返回hostent结构体地址,失败返回NULL指针


struct hostent

{

        char* h_name;                       //官方域名

        char** h_aliases;                   //可以通过多个域名访问同一主页

        int h_addrtype;                      //可以通过此变量获取保存在h_addr_list的IP地址的地址族信息

        int h_length;                          //保存IP地址长度

        char** h_addr_list;                //以整数形式保存域名对应的IP地址

}

//域名转IP时只需关注 h_addr_list 。

利用IP地址获取域名:

#include<netdb.h>

struct hostent* gethostbyaddr(const char* addr, socklen_t len, int family);//成功返回hostent结构体变量地址值,失败返回NULL指针

//addr:含有IP地址信息的in_addr结构体指针

//len:向第一个参数传递的地址信息的字节数,IPv4为4,IPv6为16

//family:传递地址族信息,IPv4为AF_INET,IPv6为AF_INET6

套接字可选项的读取和设置通过两个函数完成:

读取:

#include <sys/socket.h>

int getsockopt(int sock, int level, int optname, void* optval, socklen_t* optlen);//成功返回0,失败返回-1

//sock:用于查看选项套接字文件描述符

//level:要查看的可选项的协议层

//optname:要查看的可选项名

//optval:保存要查看结果的缓冲地址值

//optlen:向第四个参数optval传递的缓冲大小,调用函数后,该变量中保存通过第四个参数返回的可选项信息的字节数。

设置:

#include <sys/socket.h>

int setsockopt(int sock, int level, int optname, const void* optval, socklen_t optlen);//成功返回0,失败返回-1

//sock:用于更改可选项的套接字文件描述符

//level:要更改的可选项协议层

//optname:要更改的可选项名

//optval:保存要更改的选项信息的缓冲地址值

//optlen:向第四个参数optval传递的可选项信息的字节数

I/O缓冲可选项:

SO_RCVBUF是输入缓冲(write)大小相关可选项

SO_SNDBUF是输出缓冲(read)大小相关可选项

这两个可选项既可以读取当前I/O缓冲大小,也可以进行修改。



后台处理是指将控制台窗口中的指令放到后台运行的方式。如果以如下方式运行程序,则程序将在后台运行(&将触发后台处理):

./hello_world &

如果采用这种方式运行程序,即可在同一控制台输入下列命令,无需另外打开新控制台:

ps au



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值