Chinaunix首页 | 论坛 | 博客
  • 博客访问: 239537
  • 博文数量: 69
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 15
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-23 13:55
文章分类

全部博文(69)

文章存档

2016年(11)

2013年(58)

我的朋友

分类: LINUX

2016-05-09 12:16:56

原文地址:socket编程备忘录 作者:leonwang202

#include
int socket(int domain, int type, int protocol);
socket的三个形参搭配整理如下:
------------------------------------------------------------------
TCP、UDP socket: 最常用的socket,不解释

tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
udp_socket = socket(AF_INET, SOCK_DGRAM, 0);

struct sockaddr_in  inet_addr;
inet_addr.sin_family      = AF_INET;
inet_addr.sin_addr.s_addr = inet_addr("192.168.1.1");
inet_addr.sin_port        = htons(inet_port);

bind(fd, (struct sockaddr *) &inet_addr, sizeof(inet_addr));

------------------------------------------------------------------

UNIX Domain socket: 进程间通信常用

unix_socket = socket(AF_LOCAL, SOCK_STREAM, 0); 或者
unix_socket = socket(AF_LOCAL, SOCK_DGRAM, 0);

struct sockaddr_un  un_addr;

unlink("my_file_path");
un_addr.sun_family = AF_LOCAL;
strcpy(un_addr.sun_path, "my_file_path");

bind(fd, (struct sockaddr *) &un_addr, sizeof(un_addr));

----------------------------------------------------------------------
RAW socket: 用来处理IP包,它最多只能触及IP包头,再往下就搞不定了

inet_raw_fd = socket(AF_INET, SOCK_RAW, my_protocol);
struct sockaddr_in    inet_addr;


inet_addr.sin_family = AF_INET;
inet_addr.sin_addr.s_addr = inet_addr("192.168.1.1");

int on = 1;  /*  1 用户程序处理IP头,  0 用户程序不处理IP头 */
setsockopt(inet_raw_fd,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on));

形参 my_protocol,可选值为下面的任意组合
IPPROTO_UDP
IPPROTO_TCP
IPPROTO_ICMP  

IPPROTO_RAW

----------------------------------------------------------------------
AF_PACKET socket: 用来处理以太网包,它能修改以太网包头

un_cooked_fd = socket(AF_PACKET, SOCK_RAW, my_protocol); //用户程序处理 以太网头
cooked_fd = socket(AF_PACKET, SOCK_DGRAM, my_protocol);  //用户程序不处理以太网头

struct sockaddr_ll ll_addr;

struct ifreq if_idx;
strncpy(if_idx.ifr_name, "eth0", IFNAMSIZ-1);
ioctl(fd, SIOCGIFINDEX, &if_idx);

ll_addr.sll_family = AF_PACKET;
ll_addr.sll_protocol = htons(ETH_P_ALL);
ll_addr.sll_ifindex = if_idx.ifr_ifindex;

bind(sockfd, (struct sockaddr *)&ll_addr, sizeof(ll_addr)) ;

形参 my_protocol,可选值为下面的任意组合
ETH_P_IP
ETH_P_ARP
ETH_P_RARP

ETH_P_ALL

 pingpang_socket.rar  
-------------------------------------------------------------------------

NetLink socket: TODO

############################################################################
   
accept 调用前连接夭折:   
      SVR4 实现:    返回错误码
      Berkeley实现: 服务器进程看不到

服务器fork出来的子进程挂了:
    a.父进程 先前已经close了 connfd
    b.子进程 挂会导致释放 connfd
    c.connfd 描述字释放后,会导致发送 FIN 给客户端
    d.客户发 ACK
    e.客户发 数据 (引发server发RST)
    客户read此时返回(只接受到FIN,返回0,errno "server terminated prematurely" 服务器过早终止)
    f.服务 发 RST(防止主动关闭的一端经历 TIME_WAIT 状态)
    客户 read 此时返回(接受到FIN 和 RST, RST 优先级高,返回,errno "ECONNREAET" 对方复位连接)
    g.客户再发数据 (引发SIGPIPE信号)

       
服务器主机挂了
    客户发数据
    read 返回错误 完全没响应 ETIMEOUT
    中间路由器判断服务器主机不可达,发目的地不可达的ICMP消息响应,错误 EHOSTUNREACH 或 ENETUNREACH


服务器主机崩溃后重启
    服务器崩溃后重启,他的TCP丢失了连接信息
    客户发数据
        服务发RST
        客户read 返回错误 ECONNRESET

daytime 服务器 accept 后 write 后马上 close ??   

 
从进程到内核传递套接字地址结构有3个函数:bind connect sendto
他们的形参中都有 *sockaddr 和 结构大小sizeof(serv)
    bind(sockfd,(struct sockaddr *)&serv,sizeof(serv) );

从内核到进程传递套接字地址结构有4个函数:accept recvfrom getsocketname getpeername
他们的形参中都有 *sockaddr 和 指向结构大小的整数的“指针” 
    struct sockaddr_un test;
    socklen_t len = sizeof(test);
    getpeername(unixfd,(* sockaddr)&test,&len);
    /* len 可能会变,这时len代表test的写后大小*/

为何将结构大小由整数改为指向整数的指针呢?
这样len可以被函数更新
当函数被调用时,结构大小是一个值(此值告诉内核在写结构时不至于越界),
当函数返回时,结构大小又是一个结果(它告诉进程,内核在此结构中确切存储了多少信息),
这种参数类型叫做“值-结果”参数


客户在读回任何东西之前对服务器写两次,而第一次写就引发了一个RST.(第二次写生成SIGPIPE)
所用规则是:当一个进程(client)向接受了RST的套接字(server)进行写操作时,内核给该进程(client)
发一个SIGPIPE信号。此信号的缺省行为就是终止进程(client),所以进程(client)必须捕获它以免被终止

client不论是捕获了该信号并从信号处理程序返回,还是不理会该信号,client写操作都返回EPIPE错误



网络编程时可能会遇到的三种情况:
1 当派生子进程时,必须捕获信号
2 当捕获信号时,必须处理被中断的系统调用
3 SIGCHLD的信号处理程序应使用函数waitpid以免留下僵尸进程


在客户断和服务器可以通信之前,每一方都要指定连接的套接字对:
    本地IP 和 本地端口   
        两端都可以调用bind(通常客户不调,由内核选择,内核选后由getsockname探知)
    远程IP 和 远程端口   
        客户调用connect;服务器端通过accept返回这2个值。
        若accept返回后由新程序来处理,且调用exec来执行,则在新程序中必要时
        调用getpeername来确定客户的IP地址和端口号。(前提是connfd要传给exec)


关于异构系统socket通信时大端小端问题,常用解决方法:
1。所有的数值数据作为文本串来传递。当然,保证通讯两主机要有相同的字符集
2。显示定义所支持数据类型的二进制格式(位数,大端,小端)。远程过程调用(RPC)软件包常用此技术


拒绝服务型攻击
   当一个服务器正在处理多个客户时,服务器决不能阻塞于只与单个用户相关的函数调用。如果这样的话
服务器将悬挂并拒绝为所有其他客户提供服务,这叫拒绝服务(denial of service)型攻击。
   可能的解决方法:(a)使用非阻塞I/O模型
          (b)让每个客户由单独的控制线程提供服务(例如,创建子进程或线程来为每个客户提供服务)
          (c)对I/O操作设置超时

有3种方法给套接字上的I/O操作设置超时
1. 调用alarm(),这涉及到信号处理,可能与进程中其他已有的alarm调用冲突
2. 使用select阻塞在等待I/O上,select内部有一个时间限制,以此来代替在read或write调用上的阻塞
3. 使用新的SO_RECVTIMEO SO_SNDTIMEO套接字选项,但并不是所有实现都支持这两个套接字选项。


###############################################################################################

#include
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);

这连个函数和标准的read和write函数很相似,前三个参数意义和read write 相同。
只是多了一个附加的参数flags (0 或者一些宏的逻辑或)
               recv    send
MSG_DONTROUTE           Y
MSG_DONTWAIT    Y       Y
MSG_OOB         Y       Y
MSG_PEEK        Y
MSG_WAITTALL    Y

阅读(2818) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~