Linux 高性能服务器编程——Linux网络编程基础API
- 大端字节序:“高低,低高”,即,一个整数的高位字节(23~31位)存储在内存的低地址处,低位字节存储在内存的高地址处。
- 小端字节序:“高高,低低”。
#inlcude<netinet/in.h>
unsigned long int htonl( unsigned long int hostlong );
unsigned short int htons( unsigned short int hostshort );
unsigned long int ntohl( unsigned long int netlong );
unsigned short int ntohs( unsigned short int netshort );
通用socket地址(结构体)
#include <bits/socket.h>
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
}


#include <bits/socket.h>
struct sockaddr_storage
{
sa_family_t sa_family;
unsigned long int __ssalign;
char __ss_padding[128-sizeof(__ss__align)];
}
- UNIX本地域协议族专用socket地址
#inlcude<sys/un.h>
struct sockaddr_un
{
sa_family_t sin_family; /* 地址族:AF_UNIX */
char sun_path[108]; /* 文件路径名 */
};
- IPv4专用socket地址
#inlcude<sys/un.h>
struct sockaddr_in
{
sa_family_t sin_family; /* 地址族:AF_INET */
u_int16_t sin_port;
struct int_addr sin_addr; /* 文件路径名 */
};
struct in_addr
{
u_int32_t s_addr;
};
- IPv6专用socket地址
#inlcude<sys/un.h>
struct sockaddr_in6
{
sa_family_t sin6_family; /* 地址族:AF_INET6*/
u_int16_t sin_port; /* 端口号,要用网络字节序表示 */
u_int32_t sin6_flowinfo; /* 流信息,应设置为0 */
struct int6_addr sin6_addr; /* IPv6地址结构体 */
u_int32_t sin6_scope_id; / * scope Id.,尚处于试验阶段 */
};
struct in_addr
{
unsigned char sa_addr[16]; /* IPv6地址,要用网络字节序表示 */
};
#include<arpa/inet.h>
in_addr_t inet_addr( const char* strptr ); /* 点分十进制字符串表示的IPv4地址转化为用网络字节序整数表示的IPv4地址。失败时返回INADDR_NONE */
int inet_aton( const char* cp, struct in_addr* inp ); /* 完成和in_addr_t同样的功能,但是将转化结果存储于参数inp指向的地址结构中。 成功时返回1, 失败时返回0 */
char* inet_ntoa( struct in_addr in ); /* inet_ntoa函数将网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址 */
有一点需要注意,看下面的代码
char* szValue1 = inet_ntoa( "1.2.3.4" );
char* szValue2 = inet_ntoa( "10.194.71.60" );
printf( "address 1: %s\n", szValue1 );
printf( "address 2: %s\n", szValue2 ); //打印结果
address1: 10.194.71.60
address2: 10.194.71.60
#include <arpa/inet.h>
int inet_pton(int af, const char* src, void* dst);
const char* inet_ntop( int af, const void* src, char* dst, socklen, cnt);
#include <netinet/in.h>
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
#include <sys/types.h>
#include <sys/socket.h>
int scoket( int domain, int type, int protocol );
#include <sys/types.h>
#include <sys/socket.h>
int bind( int sockfd, const struct sockaddr* my_addr, socklen_t addrlen );
- EACCES : 被绑定的地址是受保护的地址,仅超级用户能够访问。比如普通用户将socket绑定到知名服务器端口(端口号为 0~1023)上时,bind将返回EACCES 错误。
- EADDRINUSE:被绑定的地址正在使用中。比如讲socket绑定到一个处于TIME_WAIT状态的socket地址。
#include <sys/socket.h>
int listen( int sockfd, int backlog );
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h> static int stop = 0;
static void handle_term( int sig )
{
stop = 1;
} int main( int argc, char* argv[] )
{
signal( SIGTERM, handle_term ); if( argc <= 3 )
{
printf( "usage: %s ip_address port_number backlog\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] );
int backlog = atoi( argv[3] ); int sock = socket( PF_INET, SOCK_STREAM, 0 );
assert( sock >= 0 ); struct sockaddr_in address;
bzero( &address, sizeof( address ) );
address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &address.sin_addr );
address.sin_port = htons( port ); int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
assert( ret != -1 ); ret = listen( sock, backlog );
assert( ret != -1 ); while ( ! stop )
{
sleep( 1 );
} close( sock );
return 0;
}
运行:
#./telnetlisten 10.8.56.206 12345 5 //监听12345端口 backlog取值为5
#telnet 10.8.56.206 12345 //运行10次
#netstat -nt | grep 12345
#include <sys/types.h>
#include <sys/socket.h>
int accept( int sockfd, struct sockaddr *addr, socklen_t *addrlen );
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h> int main( int argc, char* argv[] )
{
if( argc <= 2 )
{
printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] ); struct sockaddr_in address;
bzero( &address, sizeof( address ) );
address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &address.sin_addr );
address.sin_port = htons( port ); int sock = socket( PF_INET, SOCK_STREAM, 0 );
assert( sock >= 0 ); int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
assert( ret != -1 ); ret = listen( sock, 5 );
assert( ret != -1 );
// 暂停20s 以等待客户端连接和相关操作(掉线或者退出)完成
sleep(20);
struct sockaddr_in client;
socklen_t client_addrlength = sizeof( client );
int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
if ( connfd < 0 )
{
printf( "errno is: %d\n", errno );
}
else
{
char remote[INET_ADDRSTRLEN ];
printf( "connected with ip: %s and port: %d\n",
inet_ntop( AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN ), ntohs( client.sin_port ) );
close( connfd );
} close( sock );
return 0;
}
具体操作如下:
#./accept 10.8.56.206 12345
#telnet 10.8.56.206 12345
在启动telnet客户端程序后,在20s内关闭telnet 客户端。结果发现accept调用能够正常返回,服务器输出如下:
[root@vm MOTO]# ./accept 10.8.56.206 12345
connected with ip: 10.8.56.201 and port: 1313
由此可见,accept 只是从监听队列中取出连接,而不论连接处于何种状态(如上图CLOSE_WAIT状态或者或者ESTABLISHED状态(断开telent客户端与服务器的网络,服务器该连接的状态)),更不关心任何网络状态的变化。
#include <sys/types.h>
#include <sys/socket.h>
int connect( int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen );
- ECONNREFUSED:目标端口不存在,连接被拒绝。
- ETIMEDOUT:连接超时。
#include <unist.h>
int close ( int fd );
#include <sys/socket.h>
int shutdown ( int sockfd, int howto );

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv ( int sockfd, void *buf, size_t len, int flags );
ssize_t send ( int sockfd, const void *buf, size_t len, int flags );
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h> int main( int argc, char* argv[] )
{
if( argc <= 2 )
{
printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] ); struct sockaddr_in server_address;
bzero( &server_address, sizeof( server_address ) );
server_address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &server_address.sin_addr );
server_address.sin_port = htons( port ); int sockfd = socket( PF_INET, SOCK_STREAM, 0 );
assert( sockfd >= 0 );
if ( connect( sockfd, ( struct sockaddr* )&server_address, sizeof( server_address ) ) < 0 )
{
printf( "connection failed\n" );
}
else
{
printf( "send oob data out\n" );
const char* oob_data = "abc";
const char* normal_data = "123";
send( sockfd, normal_data, strlen( normal_data ), 0 );
send( sockfd, oob_data, strlen( oob_data ), MSG_OOB );
send( sockfd, normal_data, strlen( normal_data ), 0 );
} close( sockfd );
return 0;
}
接收方:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h> #define BUF_SIZE 1024 int main( int argc, char* argv[] )
{
if( argc <= 2 )
{
printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] ); struct sockaddr_in address;
bzero( &address, sizeof( address ) );
address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &address.sin_addr );
address.sin_port = htons( port ); int sock = socket( PF_INET, SOCK_STREAM, 0 );
assert( sock >= 0 ); int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
assert( ret != -1 ); ret = listen( sock, 5 );
assert( ret != -1 ); struct sockaddr_in client;
socklen_t client_addrlength = sizeof( client );
int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
if ( connfd < 0 )
{
printf( "errno is: %d\n", errno );
}
else
{
char buffer[ BUF_SIZE ]; memset( buffer, '\0', BUF_SIZE );
ret = recv( connfd, buffer, BUF_SIZE-1, 0 );
printf( "got %d bytes of normal data '%s'\n", ret, buffer ); memset( buffer, '\0', BUF_SIZE );
ret = recv( connfd, buffer, BUF_SIZE-1, MSG_OOB );
printf( "got %d bytes of oob data '%s'\n", ret, buffer ); memset( buffer, '\0', BUF_SIZE );
ret = recv( connfd, buffer, BUF_SIZE-1, 0 );
printf( "got %d bytes of normal data '%s'\n", ret, buffer ); close( connfd );
} close( sock );
return 0;
}
接收方两种结果:
[root@vm MOTO]# ./recv 10.8.56.206 12345
got 5 bytes of normal data '123ab'
got 1 bytes of oob data 'c'
got 3 bytes of normal data '123'
或者:
[root@vm MOTO]# ./recv 10.8.56.206 12345
got 3 bytes of normal data '123'
got -1 bytes of oob data ''
got 2 bytes of normal data 'ab'
客户端发送的"123"到服务器的接收缓冲区,立马被服务器读走,然后客户端发送的带外数据"abc"到服务器的接收缓冲区,当服务器调用recv( connfd, buffer, BUF_SIZE-1, MSG_OOB );时失败,因为第一个字符不是带外数据,而是字符"a",真正的带外数据是"c"。服务器接着读走了"ab"字符后关闭了连接,但是发送的不是结束报文段,发送的是复位报文段,即如果recv( connfd, buffer, BUF_SIZE-1, MSG_OOB );失败时,最后发送的不是结束报文段,是复位报文段。
[root@vm MOTO]# ./send 10.8.56.206 12345
send oob data out
- I/O复用产生的异常事件。
- SIGURG信号。
#include <sys/socket.h> int sockatmark(int fd);
sockatmark判断sockfd是否处于带外标记,即下一个被读到的数据是否是带外数据,如果是,sockatmark返回1,此时我们就是利用带MSG_OOB标志的recv调用来接收带外数据。如果不是,则seckatmark返回0。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom ( int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen );
ssize_t sendto ( int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen );
#include <sys/socket.h>
ssize_t recvmsg( int sockfd, struct msghdr* msg, int flags );
ssize_t sendmsg( int sockfd, struct msghdr* msg, int flags );
struct msghdr
{
void* msg_name; /* socket地址 */
socklen_t msg_namelen; /* socktet地址的长度 */
struct iovec* msg_iov; /* 分散的内存块 */
int msg_iovlen; /* 分散的内存块数量 */
void msg_control; /* 指向辅助数据的起始位置 */
socklen_t msg_controllen; /* 辅助数据的大小 */
int msg_flags; /* 复制函数中的flags参数,并在调用过程中更新 */
}; struct iovec
{
void *iov_base; /* 内存起始地址 */
size_t iov_len; /* 这块内存的长度 */
};
由上可见,iovec结构体封装了一块内存的起始位置和长度。msg_iovlen指定这样的iovec结构对象有多少个。对于recvmsg而言,数据将被读取并存放在msg_iovlen块分散的内存中,这些内存的位置和长度则由msg_iov指向的数组指定,这称为分散读;对于sendmsg而言,msg_iovlen块分散内存中的数据将被一并发送,这称为集中写。
#include <sys/socket.h>
int getsockname ( int sockfd, struct sockaddr* address, socklen_t* address_len ); /* 获取sockfd本端socket地址,并将其存储于address参数指定的内存中 */
int getpeername ( int sockfd, struct sockaddr* address, socklen_t* address_len ); /* 获取sockfd对应的远端socket地址,其参数及返回值的含义与getsockname的参数及返回值相同 */
Linux 高性能服务器编程——Linux网络编程基础API的更多相关文章
- Linux 高性能服务器编程——TCP/IP协议族
1 TCP/IP协议族体系结构 数据链路层: 职责:实现网卡接口的网络驱动程序,一处理数据在物理媒介(如以太网.令牌环等)上的传输. 常用协议:ARP协议(地址解析协议),RARP协议 ...
- linux高性能服务器编程
<Linux高性能服务器编程>:当当网.亚马逊 目录: 第一章:tcp/ip协议族 第二章:ip协议族 第三章:tcp协议详解 第四章:tcp/ip通信案例:访问Internet 第五章: ...
- linux高性能服务器编程 (一) --Tcp/Ip协议族
前言: 在学习swoole入门基础的过程中,遇到了很多知识瓶颈,比方说多进程.多线程.以及进程池和线程池等都有诸多的疑惑.之前也有学习相关知识,但只是单纯的知识面了解.而没有真正的学习他们的来龙去脉. ...
- Linux 高性能服务器编程——多线程编程
问题聚焦: 在简单地介绍线程的基本知识之后,主要讨论三个方面的内容: 1 创建线程和结束线程: 2 读取和设置线程属性: 3 线程同步方式:POSIX信号量,互斥锁和条件变量 ...
- Linux 高性能服务器编程——多进程编程
问题聚焦: 进程是Linux操作系统环境的基础. 本篇讨论以下几个内容,同时也是面试经常被问到的一些问题: 1 复制进程映像的fork系统调用和替换进程映像的exec系列系统调 ...
- Linux 高性能服务器编程——I/O复用
问题聚焦: 前篇提到了I/O处理单元的四种I/O模型. 本篇详细介绍实现这些I/O模型所用到的相关技术. 核心思想:I/O复用 使用情景: 客户端程序要同时处理多个socket ...
- Linux 高性能服务器编程——高性能服务器程序框架
问题聚焦: 核心章节. 服务器一般分为如下三个主要模块:I/O处理单元(四种I/O模型,两种高效事件处理模块),逻辑单元(两种高效并发模式,有效状态机)和存储单元(不讨论). 服务器模 ...
- Linux 高性能服务器编程——Linux服务器程序规范
问题聚焦: 除了网络通信外,服务器程序通常还必须考虑许多其他细节问题,这些细节问题涉及面逛且零碎,而且基本上是模板式的,所以称之为服务器程序规范. 工欲善其事,必先利其器,这篇主要来探 ...
- Linux 高性能服务器编程——TCP协议详解
问题聚焦: 本节从如下四个方面讨论TCP协议: TCP头部信息:指定通信的源端端口号.目的端端口号.管理TCP连接,控制两个方向的数据流 TCP状态转移过程:TCP连接的任意一 ...
随机推荐
- [LeetCode] Self Dividing Numbers 自整除数字
A self-dividing number is a number that is divisible by every digit it contains. For example, 128 is ...
- [LeetCode] Find Bottom Left Tree Value 寻找最左下树结点的值
Given a binary tree, find the leftmost value in the last row of the tree. Example 1: Input: 2 / \ 1 ...
- “百度杯”CTF比赛 九月场_SQL
题目在i春秋ctf大本营 题目一开始就提醒我们是注入,查看源码还给出了查询语句 输入测试语句发现服务器端做了过滤,一些语句被过滤了 试了一下/**/.+都不行,后来才发现可以用<>绕过 接 ...
- svg从入门到装逼(一)
svg文件是基于xml的矢量图,而canvas是基于html和js的位图.关于两者的比较,在粗就不赘述了. 1. 首先来上一个svg的基本结构: <?xml version="1.0 ...
- [SHOI 2008]Debt 循环的债务
Description 题库链接 A 欠 B \(x_1\) 元, B 欠 C \(x_2\) 元, C 欠 A \(x_3\) 元.现每人手上各有若干张 100,50,20,10,5,1 钞票.问至 ...
- 计蒜客NOIP模拟赛(2)D1T3 深黑幻想
[问题描述] 凡终于发愤图强,决定专心搞OI,不再玩纸牌和坑钱了!没过多久就飘飘然了,总是陷入自己进了集训队的深黑幻想之中. 样听说了之后,决定考一考凡欧拉回路怎么写.样:“我给你出一道题 ...
- 2015 ICL, Finals, Div. 1 Ceizenpok’s formula(组合数取模,扩展lucas定理)
J. Ceizenpok’s formula time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- 对中断的理解handle_level_irq【原创】
如下为本人原创,在解决问题的过程中的一点心得,如果有描述不准确的地方还请各位指出,非常感谢 Linux内核版本:linux-4.9.18 曾有一次调试触摸屏的时候遇到如下的问题 /startup/mo ...
- TensorFlow 中文资源全集,官方网站,安装教程,入门教程,实战项目,学习路径。
Awesome-TensorFlow-Chinese TensorFlow 中文资源全集,学习路径推荐: 官方网站,初步了解. 安装教程,安装之后跑起来. 入门教程,简单的模型学习和运行. 实战项目, ...
- 以太坊区块链Java(EthereumJ)学习笔记:概述
本系列文章介绍以太坊区块链基于Java语言的解决方案.通过介绍EthereumJ定义的主要模块和Class,希望为大家学习和使用EthereumJ提供一些帮助. 整体架构 以太坊的Java解决方案主要 ...