Unix / Linux 网络编程基础函数

int socket(int family, int type, int protocol);
/**
第一个参数为协议族:AF_INET为IPv4协议;
AF_LOCAL/AF_UNIX为Unix域、AF_ROUTE为路由、AF_INET6为IPv6协议;
第二个参数为套接字类型:SOCK_STREAM字节流、SOCK_DGRAM数据报;
第三个参数为传输协议:IPPROTO_TCP为tcp、IPPROTO_UDP为udp、IPPROTO_SCTP为sctp;
*/
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
/**
返回套接字描述符,第二、三个参数是套接字地址结构指针(包含服务器IP和端口)和长度;
之前不是必须要调用bind,内核会确定源IP地址并选择临时端口作为源端口;
若未收到SYN确认,重发时间间隔6s、24s、75s、返回错误;
若收到RST,三种情况:1)指定端口没有进程等待与之连接,硬错误;2)TCP想取消一个已有的连接;
3)TCP接收到一个根本不存在的连接上的分节;
返回错误可能是客户发出的SYN在某个路由引发目的地不可达ICMP错误,软错误
将激发三次握手过程,从closed -> SYN_SENT,若成功返回ESTABLISHED。
*/
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
/**
本地协议地址赋予一个套接字;第二、三个参数是协议地址结构指针(可以指定IP和端口,也可以不指定)和长度;
若没有绑定IP地址,内核将客户发送的SYN目的IP作为服务器源IP;
若指定端口为0,内核在bind被调用是选择一个临时端口(INADDR_ANY == 0);
*/
int listen(int sockfd, int backlog);
/**
listen仅为TCP服务器调用:1)把一个未连接的套接字转化为被动套接字,指示内核接收请求,CLOSED -> LISTEN;
2)规定内核应该为相应套接字排队的最大连接个数;
监听套接字两个队列:1)未完成连接队列,状态为SYN_RCVD,等待完成TCP三次握手;
2)已完成连接队列,状态为ESTABLISHED,已完成TCP三次握手;
backlog没有正式被定义过,不一定是两个队列总和的最大值,Berkeley给其增设一个模糊因子:backlog * 1.5;
不要把backlog设置为0;
当一个SYN到达,队列已满,将忽略;
理解SYN泛滥攻击;
*/
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
/**
accept发生在服务器端,若成功,返回值是一个由内核自动生成的全新描述符,
代表返回客户端描述符,区分第一个参数监听描述符;
最多返回三个值,其中还包括客户进程协议地址和该地址大小(可为NULL);
显示地址:例子
*/
#include <time.h>
#include <sys/socket.h>
/*server.cpp 获得服务器时间的简单迭代服务器例子*/
/*注意不能直接copy例子使用,观察套接字相关函数首字母都是大写的,有做相关处理 */
int main(int argc, char **argv)
{
    int listenfd, connfd;
    socklen_t len;
    struct sockaddr_in servaddr, cliaddr;
    char buf[MAXLEN];
    time_t ticks;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(13);

    Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));

    Listen(listenfd, LISTENQ);

    for ( ; ; ) {
        len = sizeof(cliaddr);
        connfd = Accept(listenfd, (SA *)&cliaddr, &len);
        printf("connection from %s port %d\n",
               inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
               ntohs(cliaddr.sin_port));
        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
        Write(connfd, buff, strlen(buff));
        Close(connfd);
    }
    return 0;
}
#include <unistd.h>
pid_t fork(void);
/**
Unix派生新进程唯一方法;调用一次,返回两次;
子进程:返回0;父进程:返回子进程pid;
accept后fork子进程和父进程都得到已连接的套接字;(可close父进程的连接,子进程read/write)
用法:1)创建副本;2)一个进程执行另一个进程,fork后,子进程调用exec,可用于并发服务器

框架:
for ( ; ;) {
    connfd = Accept(listenfd, ...);
    if ( (pid = Fork()) == 0) { //子进程
        Close(listenfd);        //关闭监听
        doit(connfd);           //处理请求
        Close(connfd);          //关闭套接字连接
        exit(0);                //退出
    }
    Close(connfd);     //父进程关闭子进程套接字
}
*/
int close(int sockfd);
/**
六个exec函数:execl、execv、execle、execve、execlp,ececvp;
1)execve是内核系统调用,其他五个都是调用execve的库函数;
关系图如下:
execlp      execl       execle
   |          |            |
execvp  ->  execv   ->  execve

*/

描述符引用计数,并发服务器中,父进程关闭子进程套接字只会使引用计数减一,未达到零不会引发TCP四次握手;
父进程必须调用close(connfd),否则,会耗尽父进程套接字描述符(有限),另外,即使子进程调用close(connfd)描述符引用计数由2 - 1 = 1,未断开TCP连接。

#include <sys/socket.h>
/**
注意两个函数最后一个参数是值-结果参数
*/
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
/**
返回某个套接字关联的本地协议地址;
没有调用bind的客户,connect成功后,getsockname返回内核赋予该连接的本地IP地址和本地端口号;
获得某个套接字的地址族;
套接字描述参数必须是已连接的套接字描述符,不是监听套接字的描述符;
例子:
int sock_to_family(int sockfd)
{
    struct sockaddr_storage ss;
    socklen_t len;
    len = sizeof(ss);
    if (getsockname(sockfd, (SA *)&ss, &len) < 0) return -1;
    return (ss.ss_family);
}
//sockaddr_storage最大的套接字地址空间,可以承载系统支持的任何套接字地址结构;
//getsockname该函数适合任何已打开的套接字描述符;

*/
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
/**
返回某个套接字关联的外地协议地址;
*/
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容