socket 基础(Linux网络编程基础API)


        网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
        
       Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。

        socket地址

      socket地址,表示一个IP地址和端口对(ip,port),它唯一地表示了使用TCP通信的一端。

       分为通用socket地址和专用socket地址。
        通用socket地址的结构体sockaddr,其定义如下:
     
#include<bits/socket.h>
struct sockadrr
{ 
    sa_family_t    sa_family;
    char           sa_data[14];
};
sa_family成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。



      sa_data 成员用于存放socket地址值,但是,不同的协议族的地址值具有不同的含义和长度,14个字节的sa_data根本无法完全容纳多数据协议族的地址值。因此,Linux定义了下面这个新的通用socket地址结构体

   
#include<bits/socket.h>
struct sockadrr_storage
{ 
    sa_family_t    sa_family;
    unsigned long int  __ss_align;
    char           __ss_padding[128-sizeof(__ss_align)];
     
};
  并采用 内存对齐(__ss_align);

专用socket地址结构体,避免通用socket地址需要执行繁琐的位操作。
UNIX本地域协议族使用如下专用socket地址结构体:
#include <sys/un.h>
struct sockaddr_un
{
     sa_family_t  sin_family;   // 地址族:AF_UNX
     char sun_path[108];        // 文件路径名
};


创建socket,socket是可读、可写、可控制、可关闭的文件描述符。

#include<sys/types.h>
#inlcude<sys/socket.h>
int socket(int domain, int type, int protocol);
domain 参数告诉系统使用哪个底层协议族,对TCP/IP协议族而言,该参数应该设置为PF_INET ( IPv4)。

type参数指定服务类型,服务类型主要有SOCK_STREAM服务(流服务)和SOCK_UGRAM(数据服务),对于TCP/IP协议族而言,其值取SOCK_STREAM表示传输层使用TCP协议,取SOCK_DGRAM表示传输层使用UDP协议。在Linux2.6.17版本之后,type参数处理服务类型值外,还可是SOCK_NONBLOCK和SOCK_CLOEXEC,分别表示将新建socket设为非阻塞的,以及用fork调用创建子进程时在子进程中关闭该socket。

protocol参数是在前两个参数构成的协议集合下,再选择一个具体的协议。不过这个值通常都是唯一的。几乎在所有情况下,我们都应该把它设置为0,表示默认协议。

调用成功时返回一个socket文件描述符,失败则返回-1 并设置errno。


命名socket


指定地址族中的socket地址。
#include <sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);



bind 将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指定该socket地址长度。


监听socket

需要创建一个存放待处理的客户连接监听列表,才能接受客户端连接
#include <sys/socket.h>
int listen(int sockfd, int backlog);


sockfd指定被监听的socket。
backlog提示内核监听队列的最大长度。一般监听队列中完整连接的上限通常比backlog值略大(backlog+1)

接受连接

系统调用从listen监听队列中接受一个连接
#include <sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockadrr *addr, socklen_t *addrlen);


sockfd 指执行过listen系统调用的监听socket。
addr用来获取被接受连接的远端socket地址,该socket地址的长度由addrlen参数指出。

accept 成功时返回一个新的连接socket,该socket唯一标识了被接受的这个连接,服务器可通过读写该socket来与被接受连接对应的客户端通信。

发起连接

如果服务器通过listen调用来被动接受连接,那么客户端通过系统调用来主动与服务器建立连接
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *serv_addr,socklen_t addrlen);


sockfd参数由socket系统调用返回一个socket,serv_addr参数是服务器监听的socket地址,addrlen是这个地址长度。
connect成功时返回0,一旦成功建立连接,sockfd就唯一地标识了这个连接,客户端就可以通过读写sockfd来与服务器通信。

关闭连接

关闭一个连接实际上就是关闭该连接对应的socket,这个可以通过如下关闭普通文件描述符的系统调用来完成
#include<unistd.h>
int close(int fd);



fd参数是待关闭的socket,不过,close系统调用并非总是立即关闭一个连接,而是将fd的引用计数减1。只有当fd的应用计数为0时才真正关闭连接。多进程程序中,一次fork系统调用默认将是父进程中打开的socket的引用计数加1,因此我们必须在父进程和子进程中都对该socket执行close调用才能将连接关闭。

如果要立即终止连接(而不是引用减1),可以使用如下shutdown系统调用。
#include <sys/socket.h>
int shutdown(int sockfd, int howto);


sockfd参数是待关闭的socket,howto参数决定了shutdown的行为,可取下表中值。

  由此可见,shutdown能够分别关闭socket上的读或写,或者都关闭,而close在关闭连接是只能讲socket上的读和写同时关闭。











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值