linux网络编程笔记[一]

来源:互联网 发布:最大熵模型 知乎 编辑:程序博客网 时间:2024/05/07 16:31

-进程

 

进程树的顶端是一个控制进程,它是一个名为init 的

程序的执行,该进程是所有用户进程的祖先。

 

重要进程建立操作:

fork,建立进程。

exec,它包括一系列的系统调用,其中每个系统调用都完成相同的功能,即通过用

一个新的程序覆盖原内存空间,来实现进程的转变。

wait,等待一个进程结束为止。

exit,终止一进程运行。

 

3.1.2进程建立

 

#include <unistd.h>

pid_t fork(void)

fork成功后,建立子进程,拷贝父进程程序,在父进程pid为非0正整数,在子进程中pid置0,出错返回-1.

 

exec它们把一个新程序装入调用进程的内存空间,来改变调用进程的执行代码,从而形成新进程,调用成功,调用进程将覆盖,然后从新程序入口开始执行,它的进程标识符与调用进程相同。而就是说新进程取代了原来的进程

 

#include <unistd.h>

int execl( const char *path, const char *arg, ...);

int execlp( const char *file, const char *arg, ...);

int execle( const char *path, const char *arg , ..., char* const envp[]);

int execv( const char *path, char *const argv[]);

int execvp( const char *file, char *const argv[]);

第一个参数,被执行程序所在文件名

第二参数及后面参数,将组成了该程序执行时参数表

 

wait,父进程在子进程结束之前,一直处于睡眠状态。

 

exit(int status)终止进程,返回0成功,返回非0出错

 

3.2.2进程的同步

 

#include <sys/wait.h>

pid_t wait(int *status)   //status是子进程通exit传送出来的出口信息

 

说明:暂停父进程执行,等待子进程执行完毕,再重启父进程,如多个子进程执行,wait等待第一个子进程结束时返回,恢复父进程执行。

 

 

3.2.3 进程终止特殊情况

 

1.子进程终止时,父进程并不正在执行wait()调用。

2.当子进程尚未终止时,父进程却终止了。

 

 

3.4守护进程

运行在后台并独立于所有终端控制之外的进程

启动方式:

1.初始化脚本/etc/rc.d启动

2.由inetd 守护程序启

3.由cron 定时启动的处理程序

4.由at 启动的处理程序

5.在终端上用nohup 启动的进程

 

3.4.3 守护进程的错误输出

#include <syslog.h>

void syslog(int priority,char *format,....)

 

priority 参数指明了进程要写入信息的等级和用途:

LOG_EMERG 0 系统崩溃(最高优先级)

LOG_ALERT 1 必须立即处理的动作

LOG_CRIT  2  危急的情况

LOG_ERR   3 错误

LOG_WARNING 4 警告

LOG_NOTICE  5 正常但是值得注意的情况(缺省)

LOG_INFO    6 信息

LOG_DEBUG   7 调试信息(最低优先级)

 

在一个进程使用syslog()的时候,应该先使用openlog()打开系统记录:

 

#include <syslog.h>

void openlog(const char *ident, int options, int facility);

 

参数ident 是一个字符串,它将被加在所有用syslog()写入的信息前。通常这个参数是

程序的名字。

 

第四章 进程通信

1.信号

2.管道

4.socket

5.共享内存

6.信号量

7.消息队列

 

 

基本概念:

* 进程阻塞,当一个进程在执行某些操作的条件得不到满足,就自动放弃CPU资源而进入休眠状态,以等待条件的满足。

* 共享资源,为计算机的内存、存储器等资源是有限的,无法为每一个进程都分配一份单独的资

源。所以系统将这些资源在各个进程间协调使用,称为共享资源

* 锁定,当某个进程在使用共享资源使用,可能需要防止别的进程对该资源的使用

 

4.2 信号

 

 

信号是UNIX 系统所使用的进程通信方法中,最古老的一种。系统使用它来通知一个

或多个进程异步事件的发生,比如键盘上某个键被按下,或者计时器到达了某个特定的事

 

*常用信号和它们的意义:

SIGHUP ,当终止一个终端时,内核就把这一种信号发送给该终端所控制的所有进程

SIGINT ,当用户按了中断键(ctrl+c)后,内核就各与该终端有关联的所有进程发送这种信号

SIGQUIT,这种信号与SIGINT 非常相似,当用户按了退出键时(为ASCII 码FS,通常为Ctrl+/),

内核就发送出这种信号。

SIGILL,当一个进程企图执行一条非法指令时,内核就发这种信号

SIGTRAP,这是一种由调试程序使用的专信号。

SIGFPE,当产生浮点错误时(比如溢出),内核发出这种信号

SIGKILL,它从一个进程发送到另一个进程,使接收到该信号的进程终止

SIGALRM,当一个定时器到时的时候,内核就向进程发这个信号

SIGTERM,这种信号是由系统提供给普通程序使用的,按照规定,它被用来终止一个进程。

SIGSTOP,这个信号使进程暂时中止运行,系统将控制权转回正在等待运行的下一个进程

SIGCHLD,子进程结束信号

 

4.2.1 信号的处理

 

#include <signal.h>

int signal(int sig, __sighandler_t handler);

第一个参数sig 指明了所要处理的信号类型,它可以取除了SIGKILL 和SIGSTOP 外

的任何一种信

handler描述了与信号关联的动作,它可以取以下三种值:

1.一个返回值为整数的函数地址。

2.SIG_IGN,这个符号表示忽略信号

3.SIG_DFL,这个符号表示恢复系统对信号的默认处理

 

在Linux 程序中常常利用SIG_IGN 和SIG_DFL 屏蔽SIGINT 和SIGQUIT 来保证执行

重要任务的程序不会被意外的中止。

 

4.2.3 信号的复位

* 在Linux 中,当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信

号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用

相应的处理函数

 

* 但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中

 

4.2.4 在进程间发送信号

#include <signal.h>

int kill(pid_t pid,int sig)

 

参数pid 指定了信号发送的对象进程,也可以是:

1.如果pid 为零,则信号被发送到当前进程所在的进程组的所有进程

2.如果pid 为-1,则信号按进程标识符从高到低的顺序发送给全部的进程

3.如果pid 小于-1,则信号被发送给标识符为pid 绝对值的进程组里的所有进程。

 

参数sig 指定发送的信号类型。它可以是任何有效的信号

 

4.2.5 系统调用alarm()和pause()

 

1. #include <unistd.h>

unsigned int alarm(unsigned int seconds);

函数唯一的参数是seconds,其以秒为单位给出了定时器的时间.

alarm()是一个简单而有用的系统调用,它可以建立一个进程的报警时钟,在时钟定时器到时的时候,用信号向程序报告函数唯一的参数是seconds,其以秒为单位给出了定时器的时间

 

2.系统调用pause()

系统调用pause()能使调用进程暂停执行,直至接收到某种信号为

int pause(void);

该调用没有任何的参数.

 

4.5 文件和记录锁定

进程在对共享资源访问

前必须进行锁定以避免其它进程对它的操作,当然在该进程访问完共享资源后也要进行解

锁操作,以使其它进程能有机会占用共享资源。

 

4.5.3 System V 的咨询锁定

#include <unistd.h>

int lockf(int fd, int function, long size);

参数:

fd,文件打开操作中获取的文件描述符

function取如下参数:

F_ULOCK为先前锁定的区域解锁

F_LOCK锁定一个区域

F_TLOCK测试并锁定一个区域

F_TEST测试一个区域是否已经上锁

size 指从文件当前位置开始的一段连续锁定区域的长度

 

4.5.4 BSD 的咨询式锁定

#include <sys/file.h>

int flock(int fd, int operation);

调用flock 有两个参数:

参数fd 是一个已打开文件的文件描述符;

参数operation 可设定为下述各值:

LOCK_SH 共享锁

LOCK_EX 互斥锁

LOCK_UN 解锁

LOCK_NB 当文件已被锁定时不阻塞

 

两种锁定方式有以下的不同:

1.System V的锁定方式是记录锁定,可以指定锁定的范围。而BSD 的锁定方式是文

件锁定,只能指定锁定文件。

2.System V 的锁定是每个进程所独有的,可以用于父子进程间的共享锁定。而BSD

的锁定方式是可以继承的,父子进程间使用的是同一锁定的,所以不能用于父子进程间的

文件共享锁定。

 

4.6 System V IPC

 

每个IPC 对象在系统内核中都有一个唯一的标识符。通过标识符内

核可以正确的引用指定的IPC 对象.。需要注意的是,标识符的唯一性只在每一类的IPC 对

象内成立。比如说,一个消息队列和一个信号量的标识符可能是相同的,但绝对不会出现

两个有相同标识符的消息队列,IPC 对象在程序中是通过关键字(key)来访问的,和IPC 对

象标识符一样,关键字也必须是唯一的。而且,要访问同一个IPC 对象,Server 和Client

必须使用同一个关键字

 

使用系统函数ftok()来生成关键字

库函数: ftok();

函数声明: key_t ftok ( char *pathname, char proj );

ftok()函数通过混合pathname 所指文件的inode 和minor device 值以及proj 的值来产生

关键字

 

4.6.1 ipcs 命令

ipcs 命令在终端显示系统内核的IPC 对象状况。

ipcs –q 只显示消息队列

ipcs –m 只显示共享内存

ipcs –s 只显示信号量

 

4.6.2 ipcrm 命令

使用ipcrm 命令强制系统删除已存在的IPC 对象。

它的命令格式如下:

ipcrm <msg | sem | shm> <IPC ID>

ipcrm 后面的参数指定要删除的IPC 对象类型,分别为消息队列(msg)、信号量(sem)

和共享内存(shm)。然后需要给出要删除对象的标识符。

 

4.7 消息队列

消息队列就是在系统内核中保存的一个用来保存消息的队列.先入先出

1.ipc_perm

系统使用ipc_perm结构来保存每个IPC对象权限信息。

 

#include <linux/ipc.h>

struct ipc_perm

{

key_t key;

ushort uid; /* owner euid and egid */

ushort gid;

ushort cuid; /* creator euid and egid */

ushort cgid;

ushort mode; /* access modes see mode flags below */

ushort seq; /* slot usage sequence number */

};

2.msgbuf

消息队列最大的灵活性在于,我们可以自己定义传递给队列的消息的数据类型的。不

过这个类型并不是随便定义的,msgbuf 结构给了我们一个这类数据类型的基本结构定义

struct msgbuf{

long mtype;//通过它来区分不同的消息数据类型

char mtext[1];//消息数据的内容

};

但Linux 系统还是对消息类型的最大长度做出了限制

#define MSGMAX 4056 /* <= 4056 */

 

3.msg

消息队列在系统内核中是以消息链表的形式出现的。而完成消息链表每个节点结构定

义的就是msg 结构。

struct msg {

struct msg *msg_next; /* next message on queue */

long msg_type;

char *msg_spot; /* message text address */

time_t msg_stime; /* msgsnd time */

short msg_ts; /* message text size */

};

 

 

 

4.8 信号量(Semaphores)

信号量简单的说就是用来控制多个进程对共享资源使用的计数器,它经常被用作一种

锁定保护机制,当某个进程在对资源进行操作时阻止其它进程对该资源的访问

1.sem

信号量对象实际是多个信号量的集合,。在Linux 系统中,这种集合是以数

组的形式实现的。数组的每个成员都是一个单独的信号量,它们在系统中是以sem 结构的

形式储存的。

 

struct sem {

short sempid; /* 成员保存了最近一次操作信号量的进程的pid */

ushort semval; /* 成员保存着信号量的计数值 */

ushort semncnt; /* 成员保存着等待使用资源的进程数目 */

ushort semzcnt; /* 成员保存等待资源完全空闲的的进程数目 */

};

 

2.semun

semun 联合在senctl()函数中使用,提供senctl()操作所需要的信息

 

#include <linux/sem.h>

union semun {

int val;

struct semid_ds *buf; 

ushort *array; 

struct seminfo *__buf; 

void *__pad;

};

3.sembuf

sembuf 结构被semop()函数(后面回讲到)用来定义对信号量对象的基本操作。

struct sembuf {

unsigned short sem_num; /* 成员为接受操作的信号量在信号量数组中的序号(数组下标)。 */

short sem_op; /* 成员定义了进行的操作(可以是正、负和零)。 */

short sem_flg; /* 是控制操作行为的标志。 */

};

如果sem_op 是负值,就从指定的信号量中减去相应的值

如果sem_op 是正值,就在指定的信号量中加上相应的值

如果sem_op 是零,那么调用semop()函数的进程就会被阻塞直到对应的信号量值为零。

 

4.semid_qs

和msgqid_ds 类似,semid_qs 结构被系统用来储存每个信号量对象的有关信息

 

struct semid_ds {

struct ipc_perm sem_perm; /* 成员保存了信号量对象的存取权限以及其他一些信息 */

__kernel_time_t sem_otime; /* 成员保存了最近一次semop()操作的时间 */

__kernel_time_t sem_ctime; /* 成员保存了信号量对象最近一次改动发生的时间 */

struct sem *sem_base;   /* 指针保存着信号量数组的起始地址 */

struct sem_queue *sem_pending; /* 指针保存着还没有进行的操作 */

struct sem_queue **sem_pending_last; /* 指针保存着最后一个还没有进行的操作*/

struct sem_undo *undo; /* 成员保存了undo 请求的数目 */

unsigned short sem_nsems; /* 成员保存了信号量数组的成员数目 */

};

 

4.8.2 有关的函数

1.semget()

使用semget()函数来建立新的信号量对象或者获取已有对象的标识符。

系统调用: semget()

函数声明: int semget ( key_t key, int nsems, int semflg);

第一个参数key值

第二个参数nsems 是信号量对象所特有的,它指定了新生成的信号量对象中信

号量的数目,也就是信号量数组成员的个数,最大限32

第三个参数创建标志,如IPC_CREAT | 0660

 

2.semop()

使用这个函数来改变信号量对象中各个信号量的状态

系统调用: semop()

函数声明: int semop ( int semid, struct sembuf *sops, unsigned nsops);

第一个参数semid 是要操作的信号量对象的标识符。

第二个参数sops 是sembuf

的数组,它定义了semop()函数所要进行的操作序列。

第三个参数nsops 保存着sops 数组的长度,也即semop()函数将进行的操作个数

 

3.semctl()函数

semctl()函数被用来直接对信号量对象进行控制

系统调用: semctl()

函数声明: int semctl ( int semid, int semnum, int cmd, union semun arg );

cmd参数:

IPC_STAT 取得信号量对象的 semid_ds 结构信息,并将其储存在arg 参数中buf 指针

所指内存中返回。

IPC_SET 用arg 参数中buf 的数据来设定信号量对象的的semid_ds 结构信息。和消

息队列对象一样,能被这个函数设定的只有少数几个参数。

IPC_RMID 从内存中删除信号量对象。

GETALL 取得信号量对象中所有信号量的值,并储存在arg 参数中的array 数组中返

回。

GETNCNT 返回正在等待使用某个信号量所控制的资源的进程数目。

GETPID 返回最近一个对某个信号量调用 semop()函数的进程的pid。

GETVAL 返回对象那某个信号量的数值。

GETZCNT 返回正在等待某个信号量所控制资源被全部使用的进程数目。

SETALL 用 arg 参数中array 数组的值来设定对象内各个信号量的值。

SETVAL 用 arg 参数中val 成员的值来设定对象内某个信号量的值。

 

 

 

 

4.9 共享内存(Shared Memory)

共享内存,简单的说就是被多个进程共享的内存。它在各种进程通信方法中是最快的,

因为它是将信息直接映射到内存中,省去了其它IPC 方法的中间步骤。

 

#include <linux/shm.h>

 

struct shmid_ds {

struct ipc_perm shm_perm; /*成员储存了共享内存对象的存取权限及其它一些信息 */

int shm_segsz; /* 成员定义了共享的内存大小(以字节为单位)。 */

__kernel_time_t shm_atime; /* 成员保存了最近一次进程连接共享内存的时间 */

__kernel_time_t shm_dtime; /* 成员保存了最近一次进程断开与共享内存的连接的时间 */

__kernel_time_t shm_ctime; /* 成员保存了最近一次shmid_ds 结构内容改变的时间 */

__kernel_ipc_pid_t shm_cpid; /* 成员保存了创建共享内存的进程的pid */

__kernel_ipc_pid_t shm_lpid; /* 成员保存了最近一次连接共享内存的进程的pid */

unsigned short shm_nattch; /* 成员保存了与共享内存连接的进程数目 */

unsigned short shm_unused; 

void *shm_unused2; 

void *shm_unused3;

};

 

4.9.2 有关的函数

系统调用: shmget()

函数声明: int shmget ( key_t key, int size, int shmflg);

第一个参数key 是共享内存的关键字;

第二个参数size 是创建的共享内存的大小,以字节为单位

第三个参数shmflg 是控制函数行为的标志量

 

成功,函数返回共享内存的标识符

 

当一个进程使用shmget()函数得到了共享内存的标识符之后,就可以使用shmat()函数

来将共享内存映射到进程自己的内存空间

 

#include <sys/shm.h>

系统调用: shmat()

函数声明: int shmat ( int shmid, char *shmaddr, int shmflg);

第一个参数是共享内存的标识符

第二个参数shmaddr 指定了共享内存映射的地址

如果指定了地址,可以给第三个参数fshmflg 指定SHM_RND 标志来强迫将内存大

小设定为页面的尺寸

映射成功后,函数返回指向映射内存的指针。

 

3.shmctl()函数

和前两个IPC 对象一样,共享内存也有一个直接对其进行操作的函数

系统调用: shmctl()

函数声明: int shmctl ( int shmqid, int cmd, struct shmid_ds *buf );

 

IPC_STAT 获得共享内存的信息。

IPC_SET  设定共享内存的信息。

IPC_RMID 删除共享内存

 

4.shmdt()函数

当一个进程不再需要某个共享内存的映射时,就应该使用shmdt()函数断开映射。

系统调用: shmdt()

函数声明: int shmdt ( char *shmaddr );

返回值: -1 on error: errno = EINVAL (Invalid attach address passed)

shmdt()函数唯一的参数是共享内存映射的指针

 

第六章Socket

 

通信模型

 

服务器                       客户端

 

 

 

socket()                   socket()

   |                               |

 bind()                         |

   |                               |

listen()                        |

   |                               |

accpet()                      |

阻塞<-------------------connet()建立连接

   |                               |

   |                               |

   |                               |

 read()<----------------write()

   |            请求                     |

   |                               |

 write()---------------->read()

   |            答应                    |

   |                               |

close()                    close()

 

 

套接字工作过程如下:

    服务器首先启动,通过调用socket()建立一个套接字,然后调用

bind()将该套接字和本地网络地址联系在一起,再调用listen()使套接字做好侦听的准备,

并规定它的请求队列的长度,之后就调用accept()来接收连接。客户在建立套接字后就可调

用connect()和服务器建立连接。连接一旦建立,客户机和服务器之间就可以通过调用read()

和write()来发送和接收数据。最后,待数据传送结束后,双方调用close()关闭套接字。

 

TCP(传输控制协议Transmission Control Protocol)以连接为基础,也就是说两台电脑

必须先建立一个连接,然后才能传输数据。事实上,发送和接受的电脑必须一直互相通讯

和联系

 

UDP(使用者数据报协议User Datagram Protocol)它是一个无连接服务,数据可以直

接发送而不必在两台电脑之间建立一个网络连接。它和有连接的TCP 相比,占用带宽少,

但是你不知道你的数据是否真正到达了你的客户端,而客户端收到的数据也不知道是否还

是原来的发送顺序。

 

数据包的装包和解包

   数据被分成一个一个的包(Packet),包的数据头(或数据尾)被第一层协议(比如TFTP

协议) 加上第一层协议数据;然后整个包(包括内部加入的TFTP 信息头)被下层协议再

次包装(比如UDP),再这之后数据包会再次被下层协议包装(比如IP 协议),最后是被

最底层的硬件层(物理层)包装上最后一层信息(Ethernet 信息头)。

   当接受端的计算机接收到这个包后,硬件首先剥去数据包中的Ethernet 信息头,然后

内核在剥去IP 和UDP 信息头,最后把数据包提交给TFTP 应用程序,由TFTP 剥去TFTP

信息头,最后得到了原始数据。

 

基本结构

 

存储套接字地址

 

struct sockaddr_in {    //(“in” 代表 “Internet”)

short int sin_family;    /* Internet地址族 */

unsigned short int sin_port;    /* 端口号 */

struct in_addr sin_addr; /* Internet地址 */

unsigned char sin_zero[8]; /* 添0(和struct sockaddr一样大小)*/

};

 

因特网地址

struct in_addr {

unsigned long s_addr;

};

 

基本转换函数

 

网络字节顺序

因为每一个机器内部对变量的字节存储顺序不同(有的系统是高位在前,底位在后,

而有的系统是底位在前,高位在后),而网络传输的数据大家是一定要统一顺序的

 

套接字字节转换程序的列表:

l htons()——“Host to Network Short”主机字节顺序转换为网络字节顺序(对无符号

短型进行操作4 bytes)

l htonl()——“Host to Network Long” 主机字节顺序转换为网络字节顺序(对无符

号长型进行操作8 bytes)

l ntohs()——“Network to Host Short “ 网络字节顺序转换为主机字节顺序(对无符

号短型进行操作4 bytes)

l ntohl()——“Network to Host Long “ 网络字节顺序转换为主机字节顺序(对无符

号长型进行操作8 bytes)

 

inet_addr(char *ip)//把一个用数字和点表示IP 地址的字符串转换成一个无符号长整型

inet_ntoa(ina.sin_addr)//IP 地址打印出来

 

基本套接字调用

 socket()

 bind()

 connect()

 listen()

 accept()

 send()

 recv()

 sendto()

 recvfrom()

 close()

 shutdown()

 setsockopt()

 getsockopt()

 getpeername()

 getsockname()

 gethostbyname()

 gethostbyaddr()

 getprotobyname()

 fcntl()

 

 

#include <sys/types.h>

#include <sys/socket.h>

int socket(int domain , int type , int protocol);

参数:

domain:AF_INET等

type:告诉内核这个socket什么类型,有SOCK_STREAM,SOCK_DGRAM等

protocol: 设置0

 

返回值:套接字描述符

 

#include <sys/types.h>

#include <sys/socket.h>

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

参数:

sockfd:是由socket()函数返回的套接字描述符

my_addr:是一个指各struct sockaddr的指针,包含有关地址信息,名称,端口,IP地址

addrlen:可以设置为sizeof(struct sockaddr);

返回值:错误-1,正确0

 

#include <sys/types.h>

#include <sys/socket.h>

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

参数:

sockfd:套接字文件描述符,由socket()函数返回的。

serv_addr:是一个存储远程计算机的IP地址和端口信息结构

addrlen:sizeof(struct sockaddr)

返回值:错误-1

 

服务器执行以下函数:

 调用 socket()函数创建一个套接字。

 调用 bind()函数把自己绑定在一个地址上。

 调用 listen()函数侦听连接。

 调用 accept()函数接受所有引入的请求。

 调用 recv()函数获取引入的信息然后调用send()回答。

 

#include <sys/socket.h>

int listen(int sockfd, int backlog);

参数:

sockfd:套接字描述符

backlog:是未经过处理的连接请求队列可以容纳的最大数目

返回值:错误-1

 

#include <sys/socket.h>

int accept(int sockfd, void *addr, int *addrlen);

参数:

socdfd:正在listen的一个套接字描述符

addr:指向struct sockaddr_in结构的指针,里面存储远程连接过来的计算机信息(IP,POST)

addrlen: sizeof(struct sockaddr_in)大小;

返回值:错误-1

 

#include <sys/types.h>

#include <sys/socket.h>

int send(int sockfd, const void *msg, int len, int flags);

send 的参数含义如下:

 sockfd 是代表你与远程程序连接的套接字描述符。

 msg 是一个指针,指向你想发送的信息的地址。

 len 是你想发送信息的长度。

 flags 发送标记。一般都设为0

返回值为真正发送数据的长度

 

记住如果send()函数的返回值小于len 的话,则你需要再次发送剩下的数据。幸运的是,如果

包足够小(小于1K),那么send()一般都会一次发送光的。

 

#include <sys/types.h>

#include <sys/socket.h>

int recv(int sockfd, void *buf, int len, unsigned int flags);

recv()的参数含义如下:

 sockfd 是你要读取数据的套接字描述符。

 buf 是一个指针,指向你能存储数据的内存缓存区域。

 len 是缓存区的最大尺寸。

 flags 是recv() 函数的一个标志,一般都为0 

 

返回它真正收到的数据长度

 

close(sockfd);

执行close()之后,套接字将不会在允许进行读操作和写操作。任何有关对套接字描述

符进行读和写的操作都会接收到一个错误。

 

#include <sys/socket.h>

int shutdown(int sockfd, int how);

它的参数含义如下:

 sockfd 是一个你所想关闭的套接字描述符.

 how 可以取下面的值。0 表示不允许以后数据的接收操作;1 表示不允许以后数据的发送操作;2 表示和close()一样,不允许以后的任何操作(包括接收,发送数据)

 

当套接字已经打开但尚未有连接的时候用setsockopt()系统调用在其上设定选项(options)

setsockopt() 调用设置选项而getsockopt()从给定的套接字取得选项。

 

#include<sys/types.h>

#include<sys/socket.h>

int getsockopt(int sockfd, int level, int name, char *value, int *optlen);

int setsockopt(int sockfd, int level, int name, char *value, int *optlen);

 

参数说明:

sockfd必须是一个已打开的套接字

level是函数使用的协议标准(TCP/IP协议使用IPPROTO_TCP,套接字标准的选项实用SOL_SOCKET)

name选项

value指向为getsockopt()函数所获取的值,setsockopt()函数所设置的值的地址

optlen指针指向一整数,该整数包含参数以字节计算的长度

 

getpeername()函数

这个函数可以取得一个已经连接上的套接字的远程信息(比如IP 地址和端口),告诉远程和你连接你究竟是谁。

#include <sys/socket.h>

int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);

sockfd是你想取得远程信息的那个套接字描述符

addr是一个指向struct sockaddr的指针

addrlen是一个int 指针,sizeof(struct sockaddr)大小

 

#include <unistd.h>

int gethostname(char *hostname, size_t size);

参数说明如下:

 hostname 是一个指向字符数组的指针,当函数返回的时候,它里面的数据就是本

地的主机的名字.

 size 是hostname 指向的数组的长度.

函数如果成功执行,它返回0,如果出现错误,则返回–1,全局变量errno 中存储着错

误代码。

 

DNS 的操作

通过一个可读性非常强的因特网名字得到这个名字所代表的IP 地址

例:

$ telnet bbs.tsinghua.edu.cn

 

#include <netdb.h>

struct hostent *gethostbyname(const char *name);

返回值出错,NULL

struct hostent{

char *h_name; //主机名称

char **h_aliases;//主机备用名称

int h_addrtype;//返回地址的类型,一般来说是"AF_INET"

int h_length;//地址的字节长度

char **h_addr;//存储主机的网络地址

};

#define h_addr h_addr_list[0]  

 

五种I/O 模式

操作模式如下:

1.阻塞I/O

2.非阻塞I/O

3.I/O多路复用

4.信号驱动I/O(SINGIO)

5.异步I/O

 

阻塞I/O

缺省的,一个套接字建立后所处于的模式就是阻塞I/O 模式。

一个进程调用recvfrom ,然后系统调用并不返回知道有数据报到达本地

系统,然后系统将数据拷贝到进程的缓存中。(如果系统调用收到一个中断信号,则它的

调用会被中断)

 

非阻塞模式 I/O

当我们将一个套接字设置为非阻塞模式,我们相当于告诉了系统内核:“当我请求的

I/O 操作不能够马上完成,你想让我的进程进行休眠等待的时候,不要这么做,请马上返

回一个错误给我

当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不听的测试是否

一个文件描述符有数据可读(称做polling)。应用程序不停的polling 内核来检查是否I/O

操作已经就绪。这将是一个极浪费CPU 资源的操作。这种模式使用中不是很普遍。

 

I/O 多路复用

当我们调用select 函数阻塞的时候,select 函数等待数据报套接字进入读就绪状态。当

select 函数返回的时候,也就是套接字可以读取数据的时候。这时候我们就可以调用recvfrom

函数来将数据拷贝到我们的程序缓冲区中。先调用select()函数或poll()函数,然后才能进行真正的读写。

多路复用的高级之处在于,它能同时等待多个文件描述符,而这些文件描述符(套接

字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回