Linux Socket 网络编程

  • 格式:docx
  • 大小:449.47 KB
  • 文档页数:26

Linux下的网络编程指的是socket套接字编程,入门比较简单。在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾。总感觉每次看的时候都有收获,但是每次看完了之后,过段时间不看,重新拾起这些知识的时候又要从头开始,所以,在这里做个笔记也算是做个模板,以后可以直接从某一个阶段开始接着玩。。。

1. socket套接字介绍

socket机制其实就是包括socket, bind, listen, connect, accept等函数的方法,其通过指定的函数实现不同的协议(IP4,IP6等)的数据在不同层之间的传输和获取等处理。其实个人理解socket就是处于应用层和TCP/IP协议之间的一个中间层,具体的数据分析,重组,拆分等操作对于应用层的网络编程者来说都是不可见的,这些都有协议栈内核实现,应用层的网络编程会通过设置socket机制中创建socket时参数不同,而接收或者发送不同类型的数据。

对于TCP/IP在这里就不过多的讲,但是需要提及的是经典的TCP/IP参考模型是分为4个层次:应用层,传输层,网络互联层,主机到网络层。标准的套接字编程主要是指TCP和UDP的网络编程,socket网络编程的模式就是分server和client,通过server端首先建立,client端联接进行通信。网络协议栈内核实现的功能主要就是在数据到达每一层时,给数据加上或者去掉协议包头,或者进行校验,数据重组,拆分等操作,最后得到我们想要的数据格式。

下面简单列一下TCP/IP参考模型中主要的协议类型(图片来自Linux网络编程)。

图1 TCP/IP 参考模型的层次结构

标准套接字分为TCP和UDP协议两种不同type的工作流程,TCP网络编程相对于UDP来说相对复杂,因为TCP是面向连接的服务,其中包括三次握手建立连接的过程,而UDP则是无连接的服务,下图介绍了TCP服务使用socket套接字建立连接的过程,以及进行数据交互的过程。图片来自/xrb66/article/details/6048399

图2 TCP 建立socket通信的流程

TCP和UDP的网络编程模式有两种,一种是服务器模式,另一种是客户端模式,因为TCP是面向连接的服务,所以在socket机制当中,TCP的服务器模式比UDP的服务器模式多了listen,accept函数,TCP客户端比UDP客户端多了connect函数。下面是TCP和UDP网络编程的两种模式流程图。下面将结合图2,3,4介绍一下TCP

socket的机制是如何实现的。

图3 TCP 服务器端与客户端通信流程

图4 UDP服务器端和客户端通信流程

2. socket套接字基本函数介绍

2.1 创建socket套接字

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

功能介绍:

在Linux操作系统中,一切皆文件,这个大家都知道,个人理解创建socket的过程其实就是一个获得文件描述符的过程,当然这个过程会是比较复杂的。可以从内核中找到创建socket的代码,并且socket的创建和其他的listen,bind等操作分离开来。socket函数完成正确的操作是返回值大于0的文件描述符,当返回小于0的值时,操作错误。同样是返回一个文件描述符,但是会因为三个参数组合不同,对于数据具体的工作流程不同,对于应用层编程来说,这些也是不可见的。

参数说明:

从socket创建的函数可以看出,socket有三个参数,family代表一个协议族,比较熟知的就是AF_INET,PF_PACKET等;第二个参数是协议类型,常见类型是SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_PACKET等;第三个参数是具体的协议,对于标准套接字来说,其值是0,对于原始套接字来说就是具体的协议值。 2.2 地址端口绑定函数bind

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

功能介绍:

bind函数主要应用于服务器模式一端,其主要的功能是将addrlen长度 struct

sockaddr类型的myaddr地址与sockfd文件描述符绑定到一起,在sockaddr中主要包含服务器端的协议族类型,网络地址和端口号等。在客户端模式中不需要使用bind函数。当bind函数返回0时,为正确绑定,返回-1,则为绑定失败。

参数说明:

bind函数的第一个参数sockfd是在创建socket套接字时返回的文件描述符。

bind函数的第二个参数是struct sockaddr类型的数据结构,由于struct

sockaddr数据结构类型不方便设置,所以通常会通过对truct sockaddr_in进行地质结构设置,然后进行强制类型转换成struct sockaddr类型的数据,下面是两种类型数据结构的定义和对应关系图。

typedef unsigned short sa_family_t;

struct in_addr {

__be32 s_addr;

};

struct sockaddr {

sa_family_t sa_family; /* address family, AF_xxx

*/

char sa_data[14]; /* 14 bytes of protocol address

*/

};

/* Structure describing an Internet (IP) socket address.

*/

#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr)

*/

struct sockaddr_in {

sa_family_t sin_family; /* Address family

*/

__be16 sin_port; /* Port number

*/

struct in_addr sin_addr; /* Internet address

*/

/* Pad to size of `struct sockaddr'.

*/

unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -

sizeof(unsigned short int) - sizeof(struct in_addr)];

};

图5 struct sockaddr_in和struct sockaddr的映射关系

bind函数的第三个参数是指定struct sockaddr类型数据的长度,因为前面讲过bind函数的第二个参数是通过设置一个较容易的数据结构,然后通过强制类型转换成struct sockaddr,实际上,第二个参数具体的数据结构的长度会根据socket创建时,设置的family协议族的不同而不同,像AF_UNIX协议族的bind函数第二个参数的数据结构应该是struct sockaddr_un,其大小和struct sockaddr_in不同。

2.3 监听本地端口listen

int listen(int sockfd, int backlog);

功能介绍:

刚开始理解listen函数会有一个误区,就是认为其操作是在等在一个新的connect的到来,其实不是这样的,真正等待connect的是accept操作,listen的操作就是当有较多的client发起connect时,server端不能及时的处理已经建立的连接,这时就会将connect连接放在等待队列中缓存起来。这个等待队列的长度有listen中的backlog参数来设定。listen和accept函数是服务器模式特有的函数,客户端不需要这个函数。当listen运行成功时,返回0;运行失败时,返回值位-1.

参数说明:

sockfd是前面socket创建的文件描述符;backlog是指server端可以缓存连接的最大个数,也就是等待队列的长度。

2.4 接受网络请求函数accept int accept(int sockfd, struct sockaddr *client_addr, socklen_t *len);

功能介绍:

接受函数accept其实并不是真正的接受,而是客户端向服务器端监听端口发起的连接。对于TCP来说,accept从阻塞状态返回的时候,已经完成了三次握手的操作。Accept其实是取了一个已经处于connected状态的连接,然后把对方的协议族,网络地址以及端口都存在了client_addr中,返回一个用于操作的新的文件描述符,该文件描述符表示客户端与服务器端的连接,通过对该文件描述符操作,可以向client端发送和接收数据。同时之前socket创建的sockfd,则继续监听有没有新的连接到达本地端口。返回大于0的文件描述符则表示accept成功,否则失败。

参数说明:

sockfd是socket创建的文件描述符;client_addr是本地服务器端的一个struct

sockaddr类型的变量,用于存放新连接的协议族,网络地址以及端口号等;第三个参数len是第二个参数所指内容的长度,对于TCP来说其值可以用sizeof(struct

sockaddr_in)来计算大小,说要说明的是accept的第三个参数要是指针的形式,因为这个值是要传给协议栈使用的。

2.5 连接目标服务器函数connect

int connect(int sock_fd, struct sockaddr *serv_addr,int addrlen);

功能介绍:

连接函数connect是属于client端的操作函数,其目的是向服务器端发送连接请求,这也是从客户端发起TCP三次握手请求的开始,服务器端的协议族,网络地址以及端口都会填充到connect函数的serv_addr地址当中。当connect返回0时说明已经connect成功,返回值是-1时,表示connect失败。

参数说明:

connect的第一个参数是socket创建的文件描述符;第二个参数是一个struct