曾经拥有的,不要忘记。不能得到的,更要珍惜。属于自己的,不要放弃。已经失去的,留作回忆。

socket基础知识

技术文档 5493浏览 0评论

1.socket基础知识
1.1 概述
socket 是TCP/IP协议的最流行的一种网络编程接口。它与TCP/IP一起最早实现于4.1BSD UNIX 系统中,主要用于传送级(TCP,UDP)编程。
socket往往称为套接口,套接口用于网络中两个通信实体间的通信,两个实体可以存在于同一机器的不同进程中或不同机器的进程中。
套接口就好像UNIX中pipe (管道),通信双方进程通过它来与对方发送或接受数据。如同pipe 用文件描述字表示一样,socket 也用文件描述字表示,也称为套接口描述字,或简称套接字。在网络编程时要用套接字表示通信的对方。但两者不同的是,pipe的通信双方在一台机器上,共用一个pipe,双方使用不同的文件描述字;而socket通信双方一般在不同机器上,因而通信双方均有一socket和对应的套接字负责通信,当然他们之间必须连接起来。
网络中每个通信实体的socket是用一个三元组标识的。三元组指的是:协议族(地址族),网络地址、和传输层端口(本文目前只介绍Ipv4)。通信双方的一个连接是用网络五元组来标识的,它是由双方相同协议族的两个本地三元组合成的。网络五元组指的是:协议族(地址族)、本地网络地址、本地端口、远程网络地址和远程端口。上述五元组往往称为全相关。而三元组往往称为半相关
套接口分为若干类型,常用的是SOCK_STREAM和SOCK_DGRAM。SOCK_STREAM是面向连接的套接字,使用的协议是TCP,通信的双方通过三次握手建立起虚拟的连接线路,通信的过程是可靠的。而SOCK_DGRAM是面向非连接的套接字,使用的协议是UDP,通信双方以数据包的方式进行通信,通信的可靠性就不能得到保证。本项目要求采用SOCK_STREAM类型。

1.2 客户机/服务器计算模式
在TCP/IP网络应用中,通信双方相互作用采用客户/服务器模式,即客户端向服务器发出请求,服务器接收到请求后提供相应的服务。一种常见的面向连接的运行方式如下:
服务器方(首先启动):
1. 打开一通信通道并告知本地主机,它愿意在某一公认地址端口上(周知口,如http为80)接受客户请求。
2. 等待客户请求到达该端口。
3. 接收并处理该请求,发送处理结果。如果是并发服务器,则要建立(fork)一新的子进程来处理这个客户请求。服务完成后,关闭这个新进程与客户的通信连接,并终止该进程。
4. 返回第二步,等待下一个客户请求。
客户方:
1. 打开一通信通道,并连接到服务器所在主机的特定端口。
2.如服务器接受了这个连接,则向它发出服务请求报文,等待并接收应答;
3. 请求结束后关闭通信通道。

从上面的运行过程可知:
1.客户与服务器进程的作用是非对称的。
2. 服务进程一般是先于客户请求启动的。只要系统运行,该进程一直存在,直到正常终止或者强迫终止。
图1-1描述了套接字编程中服务器和客户器执行的流程。
         Server端                          Client端
          Socket                            socket
        (建立套接口)                       (建立套接口)
            |                                  |
          Bind                                 |
        (本地地址绑定)                       |
            |                                  |
          Lesten                               |
         (监听)                              |
            |                                  |
          Accept  &---------------------〉connect
         (接受连接)                    (主动连接)
            |                                  |
          Recv  &-------------------------- send
         (发送数据)                     (接受连接)
            |                                  |
          Send &----------------------------recv
            |                                  |
          Close &------------------------à close                       
          (关闭连接)                    (关闭连接)
                   客户机/服务器socket通信流程

1.3              数据结构
IP4地址结构
struct in_addr
{
unsigned long s_addr;
};
in_addr用于保存IP4网络地址,它是个结构,其中只有一个域s_addr,是32位的IPv4的网络地址,以网络字节序保存。
通用的套接口地址结构
struct sockaddr
{
    unsigned short sa_family;
    char sa_data[14];
}
IPV4套接口地址结构
struct sockaddr_in
{
short int          sin_family;
unsigned short int sin_port;
struct in_addr     sin_addr;
unsigned char      sin_zero[8];
};
这两个结构(sockaddr,sockaddr_in)都用来存储套接口地址,两者具有相同的作用,但sockaddr_in结构使用起来更加方便一些。在使用的时候,一个指向sockaddr_in的指针可以声明指向一个sockaddr地结构,因而虽然在声明中socket()函数需要一个sockaddr*作为参数,但使用时也可以给他一个sockaddr_in*作为参数。
sa_family和sin_family表示所使用的协议族(也称地址族)。常用的有如下几种。对于IPv4协议要使用AF_INET。本地进程间的通信使用
       AF_LOCAL.
AF_INET
IPv4

AF_INET6
IPv6

AF_LOCAL
UNIX协议
sa_data中包含了地址和端口,两者是混杂在一起的。
而sockadd_in结构中sin_port和sin_addr提供了一个方便的手段来访问sa_data中的端口和IP地址。
sin_zero没有具体含义,其作用是为了是两个结构在内存中具有相同的尺寸。在使用sockaddr_in的时候,要将这个域清空(可以使用函数bzero( )和memset( )来实现)。

主机数据结构
       #include <netdb.h>;
       struct hostent
       {
          char   * h_name;
          char   ** h_aliases;
          int    h_addrtype;
          int     h_length;
          char   **h_addr_list;
       };
       #define h_addr    h_addr_list[0];
服务器数据结构
struct servent
{
       char   * s_name;
       char   ** s_aliases;
       int    s_port;
       char   * proto;
};

1.4 系统调用
1.  创建套接字
#include <sys/types.h>;
#include <sys/socket.h>;
int socket(int family, int type, int protocol);
这是进行套接字通信的第一步,就是创建套接口。参数family表示所使用的协议族。参数type可以是SOCK_STREAM或SOCK_DGRAM。SOCK_STREAM是指面向连接的通信,而SOCK_DGRAM是面向非连接的数据报方式。参数protocol描述的是协议种类,一般设为0。
Socket函数用于建立三元组中的协议族部分。
此系统调用如果成功就返回的是套接字,以后的所有的对此套接口的操作都需引用这个描述字。如果调用失败,就返回-1,并在全局变量errno保存错误的类型。

2.绑定套接字
#include <sys/types.h>;
#include <sys/socket.h>;
int bind(int sockfd, cost struct sockaddr * saddr,  socklen_t addrlen);
bind( )系统调用的作用是将由socket( )所创建的套接字和地址结构sockaddr的一个实例相关联,其中含有本地的信息如IP地址、端口号。
bind函数用于建立三元组中的本地IP地址和本地端口号部分。
参数sockfd是在此前用socket系统调用创建的套接字,sa是指向结构sockaddr一个实例的指针。这个实例的类型一般是sockaddr_in,在调用时需要进行强制转换(struct sockaddr *)&saddr(其中saddr是sockaddr_in类型)。参数addrlen是结构sockaddr的大小即sizeof(struct sockaddr)。其中套接字的地址和端口可以在程序中指定,也可以由系统分配。

3.监听
#include <sys/socket.h>;
int listen( int sockfd, int backlog );
用TCP进行网络通信,服务器端要经历几个状态,当调用socket( )和bind( )后,虽然套接字已经建立起来,但是其状态是CLOSED即关闭状态。要进行通信就必须将套接字打开。对于服务器端来说要被动打开,这就是listen( )的功能。
下面就可以用accept( )和connect()建立连接。这个过程其实就是TCP的“三次握手”。

4.接受连接
#include <sys/socket.h>;
int accept( int sockfd, struct sockaddr * client_addr, socklen_t *addrlen);
服务器调用accept( )来接受连接。当客户与服务器连接(connect)并且连接成功(三次握手)后accept则返回另一个套接字。这个新创建的套接字称为“连接套接字”。服务器利用这个套接字来传输数据。而前面用socket( )调用产生的套接字则称之为“监听套接字”,这个套接字专门监听来自客户端的请求。
参数sockfd是由socket( )创建的连接套接字。client_addr是指向结构sockaddr的一个实例的指针,在返回时用来存放请求连接的客户端的IP地址、端口信息。参数addrlen存放结构* client_addr的大小。
access完成后通信双方两个三元组组成的五元组就建立起来了。

5.连接服务器
#include <sys/types.h>;
#include <sys/socket.h>;
int connect(int sockfd, const struct sockaddr *serv_addr,  socklen_t addrlen);
客户端使用这个系统调用来连接目的主机(服务器)的指定端口,以便和目的主机(在 access中等待)进行通信。
sockfd 是客户机的系统调用 socket() 返回的套接字。serv_addr 指向目的地(服务器)端口和 IP 地址的数据结构 struct sockaddr。addrlen 设置为 sizeof(struct sockaddr)。
connect完成后通信双方两个三元组组成的五元组就建立起来了。

6. 发送与接受数据
#include <sys/socket.h>;
ssize_t send( int sockfd, const void * buff, size_t len,int flags);
ssize_t recv( int sockfd, void * buf, size_t len, int flags);
这两个系统调用是用在面向连接的套接字通信中,其中参数sockfd是套接字,buff是数据缓冲区地址,len是数据的长度,flags可指定进一步的控制,通常为0。(这是与read/write的不同之处,详见有关参考资料)。buf是用来存储信息的缓冲区。调用成功返回写入或读出的字节数,失败返回-1。这里返回值可以与len的值不同。

7.关闭套接字
#include <unistd.h>;
int close( int sockfd);
int shutdown( int sockfd, int how);
这两个系统调用均为关闭套接字,但shutdown更复杂一些。
参数how的含义如下:
how=0,不允许再接受信息;
how=1,不允许再发送信息;
how=2,不允许再接受或发送信息。

转载请注明:自由的风 » socket基础知识

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址