Linux网络编程基础API

具体介绍了socket地址意义极其API,在介绍数据读写API部分引入一个有关带外数据发送和接收的程序,最后还介绍了其它一些辅助API。

socket地址API

主机字节序和网络字节序

字节序分为大端字节序和小端字节序。小端字节序又被称为主机字节序,大端字节序被称为网络字节序。大端字节序是指一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。小端字节序则相反。

Linux提供例如以下四个函数完毕主机字节序与网络字节序之间的转换:

#include<arpa/inet.h>

uint32_thtonl(uint32_t hostlong);

uint16_thtons(uint16_t hostshort);

uint32_tntohl(uint32_t netlong);

uint16_tntohs(uint16_t netshort);

它们的含义明白,比方htonl将长整型(32bit)的主机字节序转化为网络字节序。这四个函数中,长整型用来转化IP地址,短整型用来转换port号。

通用socket地址

#include <bits/socket.h>

Struct sockaddr

{

Sa_family_tsa_family;

charsa_data[14];

};

sa_family是地址族类型(sa_family_t)的变量。地址族通常与协议族类型相应,常见的地址族和相应的协议族例如以下表所看到的:

协议族

地址族

描写叙述

PF_UNIX

AF_UNIX

UNIX本地域协议族

PF_INET

AF_INET

TCP/IPv4协议族

PF_INET6

AF_INET6

TCP/IPv6协议族

专用socket地址

UNIX本地域协议族专用socket地址结构体:

#include <sys/un.h>

struct sockaddr_un

{

sa_family+tsin_family;            //地址族:AF_UNIX

charsun_path[108]         //文件路径名

};

TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用socket地址结构体,他们分别用于IPv4和IPv6:

struct sockaddr_in

{

sa_family_tsin_family;            //地址族:AF_INET

u_int16_tsin_port;                   //端口号,要用网络字节序表示

structin_addr sin_addr;          //IPv4地址结构体

};

Struct in_addr

{

u_int32_ts_addr;                     //IPv4地址,要用网络字节序表示

};

Struct sockaddr_in6

{

sa_family_tsin6_family;         //地址族:AF_INET6

u_int16_tsin6_port;                //端口号,要用网路字节序表示

u_int32_tsin6_flowinfo;//流信息,应设置为0

structin6_addr;                         //IPv6地址结构体

u_int32_tsin6_scope_id;        //scope
ID,尚处实验阶段

};

Struct in6_addr

{

Unsignedchar sa_addr[16];   //IPv6地址,要用网络字节序表示

};

全部socket地址类型的变量在实际使用时都须要转化为通用socket地址类型sockaddr(强制转换就可以),由于全部socket编程接口使用的地址參数的类型都是sockarrd。

IP地址转换函数

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int inet_aton(const char *cp, structin_addr *inp);        //将点分十进制转化为网络字节序

in_addr_t inet_addr(const char *cp);     //同上,但将结果放入cp所指地址

char *inet_ntoa(struct in_addr in);                  //将网络字节序转化为点分十进制

上面三个函数用于点分十进制字符串表示的IPv4地址和用网络字节序整数(二进制数)表示的IPv4地址之间的转换。

当中inet_aton内部用一个静态变量存储转化结果,所以函数不可重入,演示样例

#include <stdio.h>
#include <sys/un.h>
#include <arpa/inet.h>
int main()
{
struct in_addr in1, in2;
in1.s_addr = inet_addr("1.2.3.4");
in2.s_addr = inet_addr("10.194.71.60");
char* szValue1 = inet_ntoa(in1);
char* szValue2 = inet_ntoa(in2);
printf("address1:%s\n",szValue1);
printf("address2:%s\n",szValue2);
return 0;
}

运行结果:

address1:10.194.71.60

address2:10.194.71.60

以下这对函数也能完毕上面3个函数的功能

#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_t size);

socket相关函数

#include <sys/types.h>         /* See NOTES */

#include <sys/socket.h>

int socket(int domain, int type, intprotocol);         //创建socket,指定协议族和服务类型

int bind(int sockfd, const struct sockaddr*addr, socklen_t addrlen);   //绑定地址

int listen(int sockfd, int backlog);   //server创建监听队列以存放处理的客户连接

int accept(int sockfd, struct sockaddr*addr, socklen_t *addrlen);/从监听队列接收一个连接

int connect(int sockfd, const structsockaddr *addr, socklen_t addrlen);//客户与server建立连接

#include <unistd.h>

int close(int fd);       //关闭连接

#include <sys/socket.h>

int shutdown(int sockfd, int how);         
//关闭连接

数据读写

#include <sys/types.h>

#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_tlen, int flags);

ssize_t recvfrom(int sockfd, void *buf,size_t len, int flags,

struct sockaddr *src_addr, socklen_t *addrlen);

ssize_t recvmsg(int sockfd, struct msghdr*msg, int flags);

ssize_t send(int sockfd, const void *buf,size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf,size_t len, int flags,

const struct sockaddr*dest_addr, socklen_t addrlen);

ssize_t sendmsg(int sockfd, const structmsghdr *msg, int flags);

演示样例:

带外数据的发送和接收:关于带外数据见http://blog.csdn.net/walkerkalr/article/details/35258523

发送带外数据:

#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 );

接收带外数据

#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;
}

同一时候在chen123上启动发送带外数据程序,在li123上启动接收带外数据程序,同一时候用tcpdump抓取这一过程中client和server交换的TCP报文。

详细操作例如以下:

li123@ubuntu:/$ sudo tcpdump -ntx -i eth0 port 54321

li123@ubuntu:/$./a.out 192.168.73.130 54321

chen123@ubuntu:/$ ./a.out 192.168.73.130 54321

server输出例如以下:

got 5 bytes of normal data '123ab'

got 1 bytes of oob data 'c'

got 3 bytes of normal data '123'

client发给server的3字节的带外数据”abc”中,仅有最后一个字符’c’被server当成真正的带外数据。而且,server对正常数据的接收将被带外数据截断,即前一部分正常数据”123ab”和兴许正常数据”123”是不能被一个recv调用所有读出的。

Tcpdump的输出内容中,和带外数据相关的输出例如以下:

IP 192.168.73.129.40643 > 192.168.73.130.54321:Flags [P.U], seq 4:7, ack 1, win 229, urg 3, options [nop,nop,TS val 4574025ecr 4573780], length 3

能够看到tcpdump输出标志U,这表示TCP报文段的头部被设置了紧急标志。urg
3是紧急偏移,塔指针带外数据在字节流中的位置的下一字节位置是7(3+4,当中4是报文段的序号值相对于初试序号值得偏移)。

其它基础API

#include <sys/socket.h>

int sockatmark(int sockfd);     //推断是否处于带外标记

int getsockname(int sockfd, struct sockaddr*addr, socklen_t *addrlen);    //获取本端sockfd相应的本端socket地址

int getpeername(int sockfd, struct sockaddr*addr, socklen_t *addrlen);    //获取sockfd相应的远端socket地址

以下两个函数用来读取和设置socket文件描写叙述符属性的方法

int getsockopt(int sockfd, int level, intoptname, void *optval, socklen_t *optlen);

int setsockopt(int sockfd, int level, intoptname, const void *optval, socklen_t optlen);

以下两个函数用来依据主机名称获取主机的完整信息和依据IP地址获取主机的完整信息

#include <netdb.h>

struct hostent *gethostbyname(const char*name);

#include <sys/socket.h>      /* for AF_INET */

struct hostent *gethostbyaddr(const void*addr, socklen_t len, int type);

以下两个函数依据名称获取某个服务的完整信息和依据port获取某个服务的完整信息

#include <netdb.h>

struct servent *getservbyname(const char*name, const char *proto);

struct servent *getservbyport(int port,const char *proto);

以下这个函数能通过主机名获得IP地址,也能通过server获得port号。

#include <netdb.h>

int getaddrinfo(const char *node, constchar *service, const struct addrinfo *hints,

struct addrinfo **res);

以下这个函数能通过socket地址同一时候获得以字符串标书的主机名和服务名。

#include <netdb.h>

int getnameinfo(const struct sockaddr *sa,socklen_t salen, char *host, size_t hostlen,

char *serv, size_tservlen, int flags);

Linux高性能server编程——Linux网络基础API及应用的更多相关文章

  1. Linux 高性能服务器编程——Linux网络编程基础API

    问题聚焦:     这节介绍的不仅是网络编程的几个API     更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系.     这节主要介绍三个方面的内容:套接字(so ...

  2. Linux 高性能服务器编程——Linux服务器程序规范

    问题聚焦:     除了网络通信外,服务器程序通常还必须考虑许多其他细节问题,这些细节问题涉及面逛且零碎,而且基本上是模板式的,所以称之为服务器程序规范.     工欲善其事,必先利其器,这篇主要来探 ...

  3. Linux高性能server编程——高级I/O函数

     高级I/O函数 pipe函数 pipe函数用于创建一个管道,实现进程间的通信. #include <unistd.h> int pipe(int pipefd[2]); 通过pipe ...

  4. Linux高性能server编程——信号及应用

     信号 信号是由用户.系统或者进程发送给目标进程的信息.以通知目标进程某个状态的改变或系统异常. Linux信号可由例如以下条件产生: 对于前台进程.用户能够通过输入特殊的终端字符来给它发送信号. ...

  5. Linux高性能server编程——I/O复用

     IO复用 I/O复用使得程序能同一时候监听多个文件描写叙述符.通常网络程序在下列情况下须要使用I/O复用技术: client程序要同一时候处理多个socket client程序要同一时候处理用户 ...

  6. Linux高性能server编程——定时器

    版权声明:本文为博主原创文章.未经博主允许不得转载. https://blog.csdn.net/walkerkalr/article/details/36869913  定时器 服务器程序通常管 ...

  7. Linux高性能server编程——多线程编程(下)

    多线程编程 条件变量 假设说相互排斥锁是用于同步线程对共享数据的訪问的话.那么条件变量则是用于线程之间同步共享数据的值. 条件变量提供了一种线程间的通信机制:当某个共享数据达到某个值得时候,唤醒等待这 ...

  8. Linux 高性能server编程——高级I/O函数

    重定向dup和dup2函数 #include <unistd.h> int dup(int file_descriptor); int dup2(int file_descriptor_o ...

  9. Linux高性能server编程——系统检測工具

    系统检測工具 tcpdump tcpdump是一款经典的抓包工具,tcpdump给使用者提供了大量的选项,泳衣过滤数据报或者定制输出格式. lsof lsof是一个列出当前系统打开的文件描写叙述符的工 ...

随机推荐

  1. CSF 中的应用程序请求路由

    编辑人员注释:本文章由 AzureCAT 团队的 Christain Maritnez 撰写. 应用程序请求路由(简称为 ARR)可能是 Microsoft 使用的技术中讨论得最少但极为重要的技术之一 ...

  2. Java集合框架的知识总结

    说明:面试准备,写的挺不错的. 转载地址: http://www.cnblogs.com/zhxxcq/archive/2012/03/11/2389611.html 1.综述 所有集合类都位于jav ...

  3. java实现冒泡排序,选择排序,插入排序,快速排序(简洁版)及性能测试

    1.冒泡排序是排序里面最简单的了,但性能也最差,数量小的时候还可以,数量一多,是非常慢的. 它的时间复杂度是O(n*n),空间复杂度是O(1) 代码如下,很好理解. public void bubbl ...

  4. 细数C++和C的差别

    C++语言是对C语言的扩展.所以熟悉C语言的人会发现.本书的第01~18章讲的内容基本上和C语言的内容差点儿相同. C++一方面对C语言的语法进行了改动.还有一方面又加入一些新的概念. C++中新增的 ...

  5. Unity5.0 RPG角色扮演历险类游戏之 森林历险记

    http://v.youku.com/v_show/id_XMTI1MjEyNjc4MA==.html? from=y1.7-1.2

  6. Android 带password输入界面的Dialog实现机制

    1.布局实现: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andr ...

  7. 深入理解java嵌套类和内部类

    一.什么是嵌套类及内部类 能够在一个类的内部定义还有一个类.这样的类称为嵌套类(nested classes),它有两种类型:静态嵌套类和非静态嵌套类.静态嵌套类使用非常少,最重要的是非静态嵌套类,也 ...

  8. ExtJS中form提交之后获取返回的json值

    simpleForm.form.doAction('submit', { url : 'editUserType', method : 'post', params : '', // 提交成功后执行s ...

  9. innerText和innerHTML的区别

    innerhtml用法 innertext用法 以及innerHTML与innertext的区别,看完这个大家以后在实际应用中,就可以选择合适的方法.尽可能的考虑到兼容性. test.innerHTM ...

  10. Ext JS学习第五天 Ext_window组件(二)

    此文用来记录学习笔记 •上一讲我们已经学过了window的使用,那么在这将中,我们将结合然后把Ext中需要注意的地方,以及组建的使用给予介绍.indow做几个Web开发的经典示例. •ExtWeb实战 ...