unix 网络编程常用函数

来源:互联网 发布:怎样做好数据分析 编辑:程序博客网 时间:2024/05/16 10:39

概述

socket() --得到文件描述符!

bind() --我们在哪个端口?

connect() --Hello!

listen() --有人给我打电话吗?

accept() --"Thank you for calling port 3490."

send() 和 recv() --Talk to me, baby!

sendto() 和 recvfrom() --Talk to me, DGRAM-style

close() 和 shutdown() --滚开!

getpeername() --你是谁?

gethostname() --我是谁?

DNS --你说“白宫”,我说 "198.137.240.100"


socket函数

功能:指定协议类型

定义:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int family, int type, int protocol);

返回值

出错: -1
成功: 套接口描述字 (socket file descriptor)(套接字)sockfd  

socket 函数指定了协议族(IPv4、IPv6或unix)和套接口类型(字节流、数据报或原

始套接口)。但并没有指定本地协议地址或远程协议地址。

理解socket

socket使用 Unix 文件描述符 (file descriptor) 和其他程序通讯的方式。

Unix 程序在执行任何形式的 I/O 的时候,程序是在读或者写一个文件描述符。

一个文件描述符只是一个和打开的文件相关联的整数。

这个文件可能是一个网络连接,FIFO,管道,终端,磁盘上的文件或者什么其他

的东西。Unix 中所有的东西是文件!因此,与 Internet 上别的程序通讯的时候,

要通过文件描述符。利用系统调用 socket()得到网络通讯的文件描述符。他返回

套接口描述符 (socket descriptor),然后再通过他来调用 send() 和 recv()。

那么为什么不用一般的调用 read() 和 write() 来通过套接口通讯?

简单的答案是:可以使用一般的函数!

详细的答案是:使用 send() 和 recv() 让你更好的控制数据传输。


connect 函数

功能:建立与TCP服务器的连接

定义:

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

//sockfd 是系统调用 socket() 返回的套接口文件描述符

serv_addr 是保存着目的地端口和 IP 地址的数据结构 struct sockaddr

//addrlen 设置为 sizeof(struct sockaddr)

connect 激发 TCP的三路握手过程

服务器必须准备好接受外来的连接。

这通过调用socket,bind和1isten函数来完成,称为被动打开(Passive open)

客户通过调用connect进行主动打开(active opn)。

这引起客户TCP发送一个SYN分节(表示同步),它告诉服务器客户将在(待建立的)

连接中发送的数据的初始序列号。

服务器必须确认客户的SYN,同时自己也得发送一个SYN分节,它含有服务器将在

同一连接中发送的数据的韧始序列号。服务器以单个分节向客户发送SYN和对客户

SYN的ACK。客户必须确认服务器的SYN。

connect 出错时的返回

出错原因 :未收到SYN的响应(服务器超时,75s)

返回值:ETIMEDOUT

用户端输出:Connection time out.
  
出错原因 :收到RST响应(Hard error)SYN到达服务器,但该服务器的无此项端口服务

返回值:ECONNREFUSE

用户端输出:Connection refused

出错原因 :ICMP错误:不可路由(soft error)(目的地不可达)

返回值:EHOSTUNREACH

用户端输出:ENETUNREACH No route to host


bind 函数

功能:给套接口分配一个本地协议地址

定义:

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *my_addr, int addrlen);

sockfd 是调用 socket 返回的文件描述符。

my_addr 是指向数据结构 struct sockaddr 的指针,保存地址(即端口和 IP 地址) 信息。

addrlen 设置为 sizeof(struct sockaddr)。

返回: 0—成功, -1---出错

让内核自动处理地址ip和端口port

my_addr.sin_port = 0; /* choose an unused port at random */
my_addr.sin_addr.s_addr = INADDR_ANY; /* use my IP address */

bind( ) 自己选择合适的端口:将0赋给 my_addr.sin_por。

自动填上他所运行的机器的 IP 地址:my_addr.sin_addr.s_addr 设置为 INADDR_ANY。


listen 函数

功能:将未连接主动套接口的转换为被动套接口,指示内核接受对该套接口的连接请求.

CLOSED --? LISTEN

定义:

#include <sys/socket.h>

int listen(int sockfd, int backlog);

sockfd 是调用 socket() 返回的套接口文件描述符。

backlog 是在进入队列中允许的连接数目。

监听套接口的两个队列

未完成连接队列(incompleted connection queue): SYN_RECV

已完成连接队列(completed connection queue): ESTABLISHED

当一个客户的SYN到达时,如两队列都满的, TCP将忽略该分节且不发RST


ACCEPT 函数

功能:在已完成队列头返回下一个已完成的连接

定义

#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *cliaddr, int* addrlen);

调用成功时返回: 1. cliaddr: 客户进程的协议地址和地址大小 2. 新套接口描述字

(已连接套接口描述字)

监听套接口描述字 listening socket descriptor

一个给定的服务器常常是只生成一个监听套接口, 且一直存在,直到该服务器关闭。

已连接套接口描述字connected socket descriptor

内核为每个被接受的客户连接创建了一个已连接套接口。当服务器完成某客户的服务时,

关闭已连接套接口。

1024以下的端口:超级用户使用


fork 函数

功能:派生新进程 create new process

定义:
#include <sys/unistd.h>

pid_t fork (void);

在子进程中返回0,在父进程中返回子进程的进程ID

出错时返回 –1,调用一次返回两次

fork的典型应用:

1.一个进程可为自己创建一个拷贝。当一个拷贝处理一个操作时,其他的拷贝可以

执行其他的任务。这是非常典型的网络服务器。

2.一个进程想执行其他的程序,由于创建新进程的唯一方法是调用fork,进程首先

调 用fork来生成一个拷贝,然后其中一个拷贝(通常为子进程)调用exec 来代替自己

去执行新程序。

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

readn和writen函数

http://hi.baidu.com/linuxkernel/blog/item/a17db8443968cd82b2b7dc99.html

int readn(int fd, void *vptr, int n) // Read "n" bytes from a descriptor
{
   int nleft;
   int nread;
char   *ptr;

ptr = vptr;
nleft = n;

while (nleft > 0) {
if ( (nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR){
                   nread = 0;      // and call read() again
      }
      else   return (-1);
}
   else if (nread == 0)
             break;             // EOF

nleft -= nread;
ptr += nread;
}

return (n - nleft);        //return >= 0
}

                   
int writen(int fd, const void *vptr, int n) // Write "n" bytes to a descriptor.
{
int nleft;
int nwritten;
const char *ptr;

ptr = vptr;
nleft = n;
while (nleft > 0) {
   if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
   if (errno == EINTR){
   nwritten = 0; // and call write() again
     } else    return (-1);   // error
   }

   nleft -= nwritten;
   ptr += nwritten;
}
return (n);
}

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

1. 获取文件的信息:
stat(char* filename, struct stat* buf);
struct stat {
dev_t st_dev; /* 设备 */
ino_t st_ino; /* 节点 */
mode_t st_mode; /* 模式 */
nlink_t st_nlink; /* 硬连接 */
uid_t st_uid; /* 用户ID */
gid_t st_gid; /* 组ID */
dev_t st_rdev; /* 设备类型 */
off_t st_off; /* 文件字节数 */
unsigned long st_blksize; /* 块大小 */
unsigned long st_blocks; /* 块数 */
time_t st_atime; /* 最后一次访问时间 */
time_t st_mtime; /* 最后一次修改时间 */
time_t st_ctime; /* 最后一次改变时间(指属性) */
};

struct statfs
{
long f_type; /* 文件系统类型 */
long f_bsize; /* 块大小*/
long f_blocks; /* 块多少*/
long f_bfree; /* 空闲的块()*/
long f_bavail; /* 可用块 */
long f_files; /* 总文件节点 */
long f_ffree; /* 空闲文件节点 */
fsid_t f_fsid; /* 文件系统id */
long f_namelen; /* 文件名的最大长度 */
long f_spare[6]; /* spare for later */
};

2. 获取文件访问权限或者判断文件是否存在:
int access(char* filename, int mode);

3. 获取当前时间:
time_t t;char* asctime(localtime(&t));
或者
time(&t);char* ctime(&t);
得到的字符串形式为:Wed Mar 12 10:07:53 2003

4. 计算两个时刻之间的时间差
double difftime(time_t time2, time_t time1);

5. 删除某文件:
int unlink(char* pathname);
int remove(char* pathname);

6. 删除某目录:
int rmdir(const char* pathname);

7. 获得当前所在目录名:
char * getcwd(char *buf,size_t size); buf将会返回目前路径名称。

8. 获取目录信息:
DIR * opendir(const char * pathname);
int closedir(DIR *dir);
struct dirent * readdir(DIR *dir);
struct dirent
{
long d_ino; /* inode number */
off_t d_off; /* offset to this dirent */
unsigned short d_reclen; /* length of this d_name */
char d_name [NAME_MAX+1]; /* file name (null-terminated) */
};

9. strerror(errno);函数会返回一个指定的错误号的错误信息的字符串.

10.得到当前路径下面所有的文件(包含目录)的个数
struct dirent **namelist;
int num = scandir(".",&namelist,0,alphasort)

11./etc/ld.so.conf:包含共享库的搜索位置
查看执行文件调用了哪些共享库
shell>ldd a.out
共享库管理工具,一般在更新了共享库之后要运行该命令
shell>ldconfig

12.查看文件执行的速度
shell>time ./a.out

13.改变文件访问权限
int chmod(const char* path, mode_t mode);

14.改变文件大小
int chsize(int handle, long size);

15.把一个浮点数转换为字符串
char ecvt(double value, int ndigit, int *decpt, int *sign);

16.检测文件结束
int eof(int *handle);

17.检测流上的文件结束符
int feof(FILE *stream);

18.检测流上的错误
int ferror(FILE *stream);

19.装入并运行其它程序的函数
int execl(char *pathname, char *arg0, arg1, ..., argn, NULL);
int execle(char *pathname, char *arg0, arg1, ..., argn, NULL,
char *envp[]);
int execlp(char *pathname, char *arg0, arg1, .., NULL);
int execple(char *pathname, char *arg0, arg1, ..., NULL,
char *envp[]);
int execv(char *pathname, char *argv[]);
int execve(char *pathname, char *argv[], char *envp[]);
int execvp(char *pathname, char *argv[]);
int execvpe(char *pathname, char *argv[], char *envp[]);

20.指数函数
double exp(double x);

21. struct sockaddr
{
unsigned short sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
struct sockaddr_in
{
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
struct in_addr
{
unsigned long s_addr;
};
s_addr按照网络字节顺序存储IP地址
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。

使用的例子:
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(3490);
sa.sin_addr.s_addr = inet_addr("132.241.5.10");
bzero(&(sa.sin_zero), 8);
注意:如果sa.sin_addr.s_addr = INADDR_ANY,则不指定IP地址(用于Server程序)

22. #define UNIX_PATH_MAX 108
struct sockaddr_un
{
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 路径名 */
};

23. IP地址转换函数:
unsigned long inet_addr (const char *cp);
inet_addr将一个点分十进制IP地址字符串转换成32位数字表示的IP地址(网络字节顺序)

char* inet_ntoa (struct in_addr in);
inet_ntoa将一个32位数字表示的IP地址转换成点分十进制IP地址字符串。

这两个函数互为反函数

字节顺序转换:
htons()--"Host to Network Short"
htonl()--"Host to Network Long"
ntohs()--"Network to Host Short"
ntohl()--"Network to Host Long" 

24. 获取当前机器的CPU、内存使用情况
getrusage

25. open的使用中常用的flag和mode参数
int FILE_FLAG = O_WRONLY|O_APPEND|O_CREAT;
int FILE_MODE = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;

26. makefile中常用的符号:
预定义变量 含义
$* 不包含扩展名的目标文件名称。
$@ 目标的完整名称
$% 如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称
为 mytarget.so(image.o),则 $@ 为 mytarget.so,而 $% 为 image.o。

$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$< 第一个依赖文件的名称。
$? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚
$^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。

AR 归档维护程序的名称,默认值为 ar。
ARFLAGS 归档维护程序的选项。
AS 汇编程序的名称,默认值为 as。
ASFLAGS 汇编程序的选项。
CC C 编译器的名称,默认值为 cc。
CCFLAGS C 编译器的选项。
CPP C 预编译器的名称,默认值为 $(CC) -E。
CPPFLAGS C 预编译的选项。
CXX C++ 编译器的名称,默认值为 g++。
CXXFLAGS C++ 编译器的选项。
FC FORTRAN 编译器的名称,默认值为 f77。
FFLAGS FORTRAN 编译器的选项。

用变量object表示所有的.o文件:
objects := $(wildcard *.o)

make -n或者--just-print表示只是显示命令,但不会执行命令
make -s或者--slient表示全面禁止命令的显示
make -i或者--ignore-errors表示Makefile中所有命令都会忽略错误
make -k或者--keep-going表示如果某规则中的命令出错了,那么就终止该规则的执行,但继续执行其它规则

在makefile中直接利用shell获取变量PLAT
使用make中的一种用变量来定义变量的方法。这种方法使用的是“:=”操作符
PLAT := $(shell uname -a)

我们要定义一个变量,其值是一个空格,那么我们可以这样来:
nullstring :=
space := $(nullstring) # end of the line

FOO ?= bar含义是:
如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做,其等价于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif

foo := a.o b.o c.o
bar := $(foo:.o=.c)
我们先定义了一个“$(foo)”变量,而第二行的意思是把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,所以我们的“$(bar)”的值就是“a.c b.c c.c”。


你想在Makefile中设置参数值,你可以使用“override”指示符。其语法是:
override <variable> = <value>
override <variable> := <value>
当然,你还可以追加:
override <variable> += <more text>

make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,
但是如果Makefile中已定义了这个变量,或是这个变量由make命令行带入,
那么系统的环境变量的值将被覆盖。
如果make指定了“-e”参数,那么,系统环境变量将覆盖Makefile中定义的变量