-进程
进程树的顶端是一个控制进程,它是一个名为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()函数就可以返回