#include <sys/socket.h>
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 <sys/socket.h>
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
参考: