国嵌Linux视频课件整理(2)

进程通讯linux

信号通讯算法

信号(signal)机制是Unix系统中最为古老的进程间通讯机制,不少条件能够产生一个信号:编程

一、当用户按某些按键时,产生信号。数组

二、硬件异常产生信号:除数为0、无效的存储访问等等。这些状况一般由硬件检测到,将其通知内核,而后内核产生适当的信号通知进程,例如,内核对正访问一个无效存储区的进程产生一个SIGSEGV信号。服务器

三、进程用kill函数将信号发送给另外一个进程。网络

四、用户可用kill命令将信号发送给其余进程。数据结构

信号类型多线程

1) SIGHUP 2) SIGINT3) SIGQUIT4) SIGILL5) SIGTRAP 6) SIGIOT 7) SIGBUS并发

8) SIGFPE9) SIGKILL10) SIGUSR1 11) SIGSEGV 12) SIGUSR213) SIGPIPEsocket

14) SIGALRM 15)SIGTERM17) SIGCHLD 18) SIGCONT 19) SIGSTOP20) SIGTSTP 21) SIGTTIN 22)SIGTTOU23) SIGURG 24) SIGXCPU 25) SIGXFSZ26) SIGVTALRM 27) SIGPROF 28)SIGWINCH29) SIGIO 30) SIGPWR

下面是几种常见的信号:

§ SIGHUP: 从终端上发出的结束信号

§ SIGINT: 来自键盘的中断信号(Ctrl-C)

§ SIGKILL:该信号结束接收信号的进程

§ SIGTERM:kill 命令发出的信号

§ SIGCHLD:标识子进程中止或结束的信号

§ SIGSTOP:来自键盘(Ctrl-Z)或调试程序的中止执行信号

信号处理

当某信号出现时,将按照下列三种方式中的一种进行处理:

一、忽略此信号

大多数信号都按照这种方式进行处理,但有两种信号却决不能被忽略。它们是:

SIGKILL和SIGSTOP。这两种信号不能被忽略的缘由是:它们向超级用户提供了一

种终止或中止进程的方法。

二、执行用户但愿的动做

通知内核在某种信号发生时,调用一个用户函数。在用户函数中,执行用户但愿的处理。

三、执行系统默认动做

对大多数信号的系统默认动做是终止该进程。

信号发送

发送信号的主要函数有 kill和raise。

区别:

Kill既能够向自身发送信号,也能够向其余进程发送信号。与kill

函数不一样的是,raise函数是向进程自身发送信号。

#include<sys/types.h>

#include<signal.h>

int kill(pid_t pid,int signo)

int raise(int signo)

kill的pid参数有四种不一样的状况:

一、pid>0

将信号发送给进程ID为pid的进程。

二、pid == 0

将信号发送给同组的进程。

三、pid < 0

将信号发送给其进程组ID等于pid绝对值的进程。

四、pid ==-1

将信号发送给全部进程。

Alarm

使用alarm函数能够设置一个时间值(闹钟时间),当所设置的时间到了时,产生SIGALRM信

号。若是不捕捉此信号,则默认动做是终止该进程。

#include<unistd.h>

unsigned intalarm(unsigned int seconds)

Seconds:通过了指定的seconds秒后会产生信号SIGALRM。

每一个进程只能有一个闹钟时间。若是在调用alarm时,之前已为该进程设置过闹钟时间,而

且它尚未超时,之前登记的闹钟时间则被新值代换。

若是有之前登记的还没有超过的闹钟时间,而此次seconds值是0,则表示取消之前的闹钟。

Pause

pause函数使调用进程挂起直至捕捉到一个信号。

#include<unistd.h>

int pause(void)

只有执行了一个信号处理函数后,挂起才结束。

信号的处理

当系统捕捉到某个信号时,能够忽略该信号或是使用指定的处理函数来处理该信号,或者使用系统默认的方式。

信号处理的主要方法有两种,一种是使用简单的signal函数,另外一种是使用信号集函数组。

signal

#include<signal.h>

void (*signal (intsigno, void (*func)(int)))(int)

typedef void(*sighandler_t)(int)

sighandler_tsignal(int signum, sighandler_t handler))

Func可能的值是:

一、SIG_IGN:忽略此信号

二、SIG_DFL: 按系统默认方式处理

三、信号处理函数名:使用该函数处理

共享内存

共享内存是被多个进程共享的一部分物理内存。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的全部进程就能够马上看到其中的内容。

共享内存实现分为两个步骤:

1、建立共享内存,使用shmget函数。

2、映射共享内存,将这段建立的共享内存映射到具体的进程空间去,使用shmat函数。

int shmget( key_t key, int size, int shmflg )

key标识共享内存的键值: 0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将建立一块新的共享内存;若是key的取值为0,而参数shmflg中又设置IPC_PRIVATE这个标志,则一样会建立一块新的共享内存。

返回值:若是成功,返回共享内存标识符;若是失败,返回-1。

int shmat( int shmid, char *shmaddr, int flag)

参数:

shmid:shmget函数返回的共享存储标识符

flag:决定以什么方式来肯定映射的地址(一般为0)

返回值:若是成功,则返回共享内存映射到进程中的地址;若是失败,则返回- 1。

当一个进程再也不须要共享内存时,须要把它从进程地址空间中脱离。

int shmdt ( char*shmaddr )

消息队列

unix早期通讯机制之一的信号可以传送的信息量有限,管道则只能传送无格式的字节流,这无疑会给应用程序开发带来不便。消息队列(也叫作报文队列)则克服了这些缺点。

消息队列就是一个消息的链表。能够把消息看做一个记录,具备特定的格式。进程能够向中按照必定的规则添加新消息;另外一些进程则能够从消息队列中读走消息。

目前主要有两种类型的消息队列:POSIX消息队列以及系统V消息队列,系统V消息队列目前被大量使用。

持续性

系统V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。

键值

消息队列的内核持续性要求每一个消息队列都在系统范围内对应惟一的键值,因此,要得到一个消息队列的描述字,必须提供该消息队列的键值。

#include<sys/types.h>

#include<sys/ipc.h>

key_t ftok(char*pathname, char proj)

功能:

返回文件名对应的键值。

pathname:文件名

proj:项目名(不为0便可)

打开/建立

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgget(key_tkey, int msgflg)

返回值:与健值key相对应的消息队列描述字

key:键值,由ftok得到。

msgflg:标志位。

IPC_CREAT 建立新的消息队列

IPC_EXCL 与IPC_CREAT一同使用,表示若是要建立的消息队列已经存在,则返回错误。

IPC_NOWAIT 读写消息队列要求没法获得知足时,不阻塞。

在如下两种状况下,将建立一个新的消息队列:

若是没有与健值key相对应的消息队列,而且msgflg中包含了IPC_CREAT标志位。

key参数为IPC_PRIVATE。

发送消息

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgsnd(intmsqid,struct msgbuf*msgp,int msgsz,int msgflg)

功能:向消息队列中发送一条消息。

msqid 已打开的消息队列id

msgp 存放消息的结构

msgsz 消息数据长度

msgflg 发送标志,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。

消息格式

struct msgbuf

{

long mtype; /* 消息类型 > 0 */

char mtext[1]; /* 消息数据的首地址 */

};

接收消息

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgrcv(intmsqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg)

功能:从msqid表明的消息队列中读取一个msgtyp类型的消息,并把消息存储在msgp指向

的msgbuf结构中。在成功地读取了一条消息之后,队列中的这条消息将被删除。

信号量

信号量(又名:信号灯)与其余进程间通讯方式不大相同,主要用途是保护临界资源。进程能够根据它断定是否可以访问某些共享资源。除了用于访问控制外,还可用于进程同步。

分类

二值信号灯:信号灯的值只能取0或1,相似于互斥锁。 但二者有不一样:信号灯强调共享资源,

只要共享资源可用,其余进程一样能够修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程自己来解锁。

计数信号灯:信号灯的值能够取任意非负值。

建立/打开

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

int semget(key_tkey, int nsems, int semflg)

key:键值,由ftok得到

nsems:指定打开或者新建立的信号灯集中将包含信号灯的数目

semflg:标识,同消息队列

操做

int semop(int semid,struct sembuf *sops, unsigned nsops)

功能:对信号量进行控制。

semid:信号量集的ID

sops:是一个操做数组,代表要进行什么操做

nsops:sops所指向的数组的元素个数。

struct sembuf {

unsigned shortsem_num; /* semaphore index in array */

short sem_op; /*semaphore operation */

short sem_flg; /*operation flags */

};

sem_num:要操做的信号量在信号量集中的编号,第一个信号的编号是0。

sem_op:若是其值为正数,该值会加到现有的信号量值中,一般用于释放信号量;若是sem_op的值为负数,而其绝对值又大于信号的现值,操做 将会阻塞,直到信号值大于或等于sem_op的绝对值,一般用于获取信号量;若是sem_op的值为0,则操做将暂时阻塞,直到信号的值变为0。

Sem_flg:信号操做标志,可能的选择有两种:

IPC_NOWAIT:对信号的操做不能知足时,semop()不会阻塞,并当即返回,同时设定错误信息。

IPC_UNDO:程序结束时(不论正常或不正常)释放信号量,这样作的目的在于避免程序在异常状况下结束时未将锁定的资源解锁,形成该资源永远锁定。

 多线程

线程理论基础

线程(thread)技术早在60年代就被提出,但真正应用多线程到操做系统中去,是在80年代中

期,solaris是这方面的佼佼者。传统的Unix也支持线程的概念,可是在一个进程(process)中只容许有一个线程,这样多线程就意味着多进程。如今,多线程技术已经被许多操做系统所支持,包括Windows/NT、Linux。

为何有了进程,还要引入线程呢?使用多线程到底有哪些好处?

使用多线程的理由之一是:

和进程相比,它是一种很是“节俭”的多任务操做方式。在Linux系统下,启动一个新的进程

必须分配给它独立的地址空间,创建众多的数据表来维护它的代码段、堆栈段和数据段,这

是一种"昂贵"的多任务工做方式。运行于一个进程中的多个线程,它们之间使用相同的地址空间,并且线程间彼此切换所需的时间也远远小于进程间切换所须要的时间。据统计,一个进程的开销大约是一个线程开销的30倍左右。

使用多线程的理由之二是:

线程间方便的通讯机制。对不一样进程来讲,它们具备独立的数据空间,要进行数据的传递只能经过进程间通讯的方式进行,这种方式不只费时,并且很不方便。线程则否则,因为同一进程下的线程之间共享数据空间,因此一个线程的数据能够直接为其它线程所用,这不只快捷,并且方便。

除了以上所说的优势外,多线程程序做为一种多任务、并发的工做方式,有以下优势:

使多CPU系统更加有效。操做系统会保证当线程数不大于CPU数目时,不一样的线程运行于不一样的CPU上。

改善程序结构。一个既长又复杂的进程能够考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,须要使用头文件pthread.h,链接时须要使用库libpthread.a。

多线程程序设计

建立线程

#include<pthread.h>

intpthread_create(pthread_t * tidp,const pthread_attr_t *attr,

void*(*start_rtn)(void),void *arg)

tidp:线程id

attr:线程属性(一般为空)

start_rtn:线程要执行的函数

arg:start_rtn的参数

编译

由于pthread的库不是linux系统的库,因此在进行编译的时候要加上

-lpthread

# gcc filename-lpthread

终止线程

若是进程中任何一个线程中调用exit或_exit,那么整个进程都会终止。线程的正常退出方式有:

(1) 线程从启动例程中返回

(2) 线程能够被另外一个进程终止

(3) 线程本身调用pthread_exit函数

#include<pthread.h>

voidpthread_exit(void * rval_ptr)

功能:终止调用线程

Rval_ptr:线程退出返回值的指针。

线程等待

#include<pthread.h>

intpthread_join(pthread_t tid,void **rval_ptr)

功能:阻塞调用线程,直到指定的线程终止。

Tid :等待退出的线程id

Rval_ptr:线程退出的返回值的指针

线程标识

#include<pthread.h>

pthread_tpthread_self(void)

功能:

获取调用线程的 thread identifier

清除

线程终止有两种状况:正常终止和非正常终止。线程主动调用pthread_exit或者从线程函数中return都将使线程正常退出,这是可预见的 退出方式;非正常终止是线程在其余线程的干预下,或者因为自身运行出错(好比访问非法地址)而退出,这种退出方式是不可预见的。

不管是可预见的线程终止仍是异常终止,都会存在资源释放的问题,如何保证线程终止时能顺利的释放掉本身所占用的资源,是一个必须考虑解决的问题。

从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终

止动做(包括调用pthread_exit()和异常终止,不包括return)都将执行pthread_cleanup_push()所指定的清理函数。

#include<pthread.h>

voidpthread_cleanup_push(void (*rtn)(void *),void *arg)

功能:

将清除函数压入清除栈

Rtn:清除函数

Arg:清除函数的参数

#include<pthread.h>

voidpthread_cleanup_pop(int execute)

功能:

将清除函数弹出清除栈

参数:

Execute执行到pthread_cleanup_pop()时是否在弹出清

理函数的同时执行该函数,非0:执行; 0:不执行

线程同步

进行多线程编程,由于没法知道哪一个线程会在哪一个时候对共享资源进行操做,所以让如何保护共享资源变得复杂,经过下面这些技术的使用,能够解决线程之间对资源的竞争:

1 互斥量Mutex

2 信号灯Semaphore

3 条件变量Conditions

互斥量

为何须要互斥量:

Item * p=queue_list;

Queue_list=queue_list->next;

process_job(p);

free(p);

当线程1处理完Item *p=queue_list后,系统中止线程1的运行,改而运行线程2。线程2照样取出头节点,而后进行处理,最后释放了该节点。过了段时间,线程1从新 获得运行。而这个时候,p所指向的节点已经被线程2释放掉,而线程1对此毫无知晓。他会接着运行

process_job(p)。而这将致使没法预料的后果!

对于这种状况,系统给咱们提供了互斥量。线程在取出头节点前必需要等待互斥量,若是此时有其余线程已经得到该互斥量,那么该线程将会阻塞在这里。只有等到其余线程释放掉该互斥量后,该线程才有可能获得该互斥量。互斥量从本质上说就是一把锁, 提供对共享资源的保护访问。

建立

在Linux中, 互斥量使用类型pthread_mutex_t表示。在使用前, 要对它进行初始化:

对于静态分配的互斥量, 能够把它设置为默认的mutex对象PTHREAD_MUTEX_INITIALIZER

对于动态分配的互斥量, 在申请内存(malloc)以后, 经过pthread_mutex_init进行初始化, 而且在释放内存(free)前须要调用pthread_mutex_destroy。

#include<pthread.h>

int pthread_mutex_init(pthread_mutex_t*mutex,

constpthread_mutexattr_t *attr)

intpthread_mutex_destroy(pthread_mutex_t *mutex)

加锁

对共享资源的访问, 要使用互斥量进行加锁, 若是互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁。

intpthread_mutex_lock(pthread_mutex_t *mutex)

intpthread_mutex_trylock(pthread_mutex_t *mutex)

返回值: 成功则返回0, 出错则返回错误编号。

trylock是非阻塞调用模式, 若是互斥量没被锁住, trylock函数将对互斥量加锁, 并得到对共享资源的访问权限; 若是互斥量被锁住了,trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态。

解锁

在操做完成后,必须给互斥量解锁,也就是前面所说的释放。这样其余等待该锁的线程才有机会得到该锁,不然其余线程将会永远阻塞。

intpthread_mutex_unlock(pthread_mutex_t *mutex)

互斥量PK信号量

Mutex是一把钥匙,一我的拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。

Semaphore是一件能够容纳N人的房间,若是人不满就能够进去,若是人满了,就要等待有人出来。

对于N=1的状况,称为binary semaphore。

Binary semaphore与Mutex的差别:

1. mutex要由得到锁的线程来释放(谁得到,谁释放)。而semaphore能够由其它线程释放

2. 初始状态可能不同:mutex的初始值是1 ,而semaphore的初始值多是0(或者为1)。

1.2.2 解决默认壁纸是动态壁纸的问题

终于找到默认壁纸没法改变的缘由了:

在/vendor/samsung/smdkv210/overlay里对一些系统参数进行的重载配置,

故在原来的地方改默认壁纸是无效的。

 网络编程

TCP/IP协议

TCP/IP协议族

TCP/IP 实际上一个协同工做的通讯家族,为网络数据通讯提供通路。为讨论方即可TCP/IP 协议组大致上分为三部分:

• Internet 协议(IP)

• 传输控制协议(TCP)和用户数据报协议(UDP)

• 处于 TCP 和 UDP 之上的一组应用协议。它们包括:TELNET,文件传送协议(FTP),域名服务(DNS)和简单的邮件传送程序(SMTP)等。

网络层

第一部分称为网络层。主要包括Internet 协议(IP)、网际控制报文协议(ICMP)和地址解析协议(ARP):

• Internet 协议(IP)

该协议被设计成互联分组交换通讯网,以造成一个网际通讯环境。它负责在源主机和目的地主机之间传输来自其较高层软件的称为数据报文的数据块,它在源和目的地之间提供非链接型传递服务。

• 网际控制报文协议(ICMP)

它实际上不是IP层部分,但直接同IP层一块儿工做,报告网络上的某些出错状况。容许网际路由器传输差错信息或测试报文。

• 地址解析协议(ARP)

ARP 实际上不是网络层部分,它处于IP和数据链路层之间,它是在32位IP地址和48位物理地址之间执行翻译的协议。

传输层协议

第二部分是传输层协议,包括传输控制协议和用户数据报文协议。

•传输控制协议(TCP):

该协议对创建网络上用户进程之间的对话负责,它确保进程之间的可靠通讯,所提供的功能以下:

1. 监听输入对话创建请求

2. 请求另外一网络站点对话

3. 可靠的发送和接收数据

4.适度的关闭对话

用户数据报文协议(UDP):

UDP 提供不可靠的非链接型传输层服务,它容许在源和目的地之间传送数据,而没必要在传送数据以前创建对话。它主要用于那些非链接型的应用程序,如:视频点播。

应用协议

这部分主要包括Telnet,文件传送协议(FTP 和TFTP),简单文件传送协议(SMTP)和域名服务(DNS)等协议。

IP协议

IP主要有如下四个主要功能:

• 数据传送

• 寻址

• 路由选择

• 数据报文的分段

IP的主要目的是为数据输入/输出网络提供基本算法,为高层协议提供无链接的传送服务。这意味着在IP将数据递交给接收站点之前不在传输站点和接收站点之间创建对话。它只是封装和传递数据,但不向发送者或接收者报告包的状态,不处理所遇到的故障。

IP包由IP协议头与协议数据两部分构成。

TCP协议

TCP是重要的传输层协议,目的是容许数据同网络上的其余节点进行可靠的交换。它能提供端口编号的译码,以识别主机的应用程序,并且完成数据的可靠传输。

•TCP 协议具备严格的内装差错检验算法确保数据的完整性。

•TCP 是面向字节的顺序协议,这意味着包内的每一个字节被分配一个顺序编号,并分配给每包一个顺序编号。

UDP协议

UDP也是传输层协议,它是无链接的,不可靠的传输服务。当接收数据时它不向发送方提供确认信息,它不提供输入包的顺序,若是出现丢失包或重份包的状况,也不会向发送方发出差错报文。因为它执行功能时具备较低的开销,于是执行速度比TCP快。

linux socket编程

Socket

Linux中的网络编程经过Socket(套接字)接口实现,Socket是一种文件描述符。

套接字socket有三种类型:

• 流式套接字(SOCK_STREAM)能够提供可靠的、面向链接的通信流。它使用了TCP协议。TCP保证了数据传输的正确性和顺序性。

• 数据报套接字(SOCK_DGRAM)字定义了一种无链接的服务,数据经过相互独立的报文进行传输,是无序的,而且不保证可靠,无差错,它使用数据报协议UDP。

• 原始套接字容许对低层协议如IP或ICMP直接访问,主要用于新的网络协议的测试等。

地址结构

struct sockaddr

{

u_short sa_family;

char sa_data[14];

}

Sa_family:地址族,采用“AF_xxx”的形式,如:AF_INET。

Sa_data:14字节的特定协议地址。

struct sockaddr_in

{

short intsin_family; /* Internet地址族 */

unsigned short intsin_port; /* 端口号 */

struct in_addrsin_addr; /* IP地址 */

unsigned charsin_zero[8]; /* 填0 */

}

编程中通常并不直接针对sockaddr数据结构操做,而是使用与sockaddr等价的sockaddr_in数据结构

struct in_addr

{

unsigned longs_addr;

}

S_addr: 32位的地址。

地址转换

IP地址一般由数字加点(192.168.0.1)的形式表示,而在struct in_addr中使用的是IP地址是由32位的整数表示的,为了转换咱们可使用下面两个函数:

int inet_aton(constchar *cp,struct in_addr *inp)

char*inet_ntoa(struct in_addr in)

函数里面 a 表明 ascii n 表明network.第一个函数表示将a.b.c.d形式的IP转换为32位的IP,存储在 inp指针里面。第二个是将32位IP转换为a.b.c.d的格式。

字节序转换

为何要进行字节序转换?

例:

INTEL的CPU使用的小端字节序MOTOROLA 68k系列CPU使用的是大端字节序 MOTOROLA发一个16位数据0X1234给INTEL, 传到INTEL时 ,就被INTEL解释为0X3412 。

不一样类型的 CPU 对变量的字节存储顺序可能不一样:有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后,而网络传输的数据顺序是必定要统一的。因此当内部字节存储顺序和网络字节顺序不一样时,就必定要进行转换。

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操做系统

等无关,从而能够保证数据在不一样主机之间传输时可以被正确解释。网络字节顺序采用

big endian排序方式。

htons把unsigned short类型从主机序转换到网络序

htonl把unsigned long类型从主机序转换到网络序

ntohs把unsigned short类型从网络序转换到主机序

ntohl把unsigned long类型从网络序转换到主机序

IP与主机名

在网络上标识一台机器能够用IP,也可使用主机名。

struct hostent*gethostbyname(const char *hostname)

struct hostent

{

char *h_name; /* 主机的正式名称 */

char *h_aliases; /* 主机的别名 */

int h_addrtype; /* 主机的地址类型 AF_INET*/

int h_length; /* 主机的地址长度 */

char **h_addr_list;/* 主机的IP地址列表 */

}

#define h_addrh_addr_list[0] /* 主机的第一个IP地址*/

函数

进行Socket编程的经常使用函数有:

• socket 建立一个socket。

• bind 用于绑定IP地址和端口号到socket。

• connect 该函数用于绑定以后的client端,与服务器创建链接。

• listen 设置能处理的最大链接要求,Listen()并未开始接收连线,只是设置socket为listen模式。

• accept 用来接受socket链接。

• send 发送数据

• recv 接收数据

基于TCP-服务器

1. 建立一个socket,用函数socket()

2. 绑定IP地址、端口等信息到socket上,用函数bind()

3. 设置容许的最大链接数,用函数listen()

4. 接收客户端上来的链接,用函数accept()

5. 收发数据,用函数send()和recv(),或者read()和write()

6. 关闭网络链接

基于TCP-客户端

1. 建立一个socket,用函数socket()

2. 设置要链接的对方的IP地址和端口等属性

3. 链接服务器,用函数connect()

4. 收发数据,用函数send()和recv(),或者

read()和write()

5. 关闭网络链接

基于UDP-服务器

1. 建立一个socket,用函数socket()

2. 绑定IP地址、端口等信息到socket上,用函数bind()

3. 循环接收数据,用函数recvfrom()

4. 关闭网络链接

基于UDP-客户端

1. 建立一个socket,用函数socket()

2. 绑定IP地址、端口等信息到socket上,用函数bind()

3. 设置对方的IP地址和端口等属性

4. 发送数据,用函数sendto()

5. 关闭网络链接

服务器模型

在网络程序里面,通常来讲都是许多客户对应一个服务器,为了处理客户的请求, 对服务端的程序就提出了特殊的要求。目前最经常使用的服务器模型有:

•循环服务器:服务器在同一个时刻只能够响应一个客户端的请求

•并发服务器:服务器在同一个时刻能够响应多个客户端的请求

UDP循环服务器

UDP循环服务器的实现方法:UDP服务器每次从套接字上读取一个客户端的请求->处理->而后将结果返回给客户机。

socket(...);

bind(...);

while(1)

{

recvfrom(...);

process(...);

sendto(...);

}

由于UDP是非面向链接的,没有一个客户端能够总是占住服务端, 服务器对于每个客户机的请求老是可以知足。

TCP循环服务器

TCP服务器接受一个客户端的链接,而后处理,完成了这个

客户的全部请求后,断开链接。算法以下:

socket(...);

bind(...);

listen(...);

while(1)

{

accept(...);

process(...);

close(...);

}

TCP循环服务器一次只能处理一个客户端的请求。只有在这个客户的全部请求都知足后, 服务器才能够继续后面的请求。这样若是有一个客户端占住服务器不放时,其它的客户机都不能工做了,所以,TCP服务器通常不多用循环服务器模型的。

TCP并发服务器

并发服务器的思想是每个客户机的请求并不禁服务器直接处

理,而是由服务器建立一个 子进程来处理。算法以下:

socket(...);

bind(...);

listen(...);

while(1) {

accept(...);

if(fork(..)==0) {

process(...);

close(...);

exit(...);

}

close(...);

}

TCP并发服务器能够解决TCP循环服务器客户机独占服务器的状况。但同时也带来了问题:为了响应客户的请求,服务器要建立子进程来处理,而建立子进程是一种很是消耗资源的操做。

多路复用I/O

阻塞函数在完成其指定的任务之前不容许程序继续向下执行。例如:当服务器运行到accept语句时,而没有客户请求链接,服务器就会中止在 accept语句上等待链接请求的到来。这种状况称为阻塞(blocking),而非阻塞操做则能够当即完成。例如,若是你但愿服务器仅仅检查是否有客户 在等待链接,有就接受链接,不然就继续作其余事情,则能够经过使用select系统调用来实现。除此以外,select还能够同时监视多个套接字 。

int select(intmaxfd, fd_set *readfds, fd_set *writefds, fe_set

*exceptfds, conststruct timeval *timeout)

Maxfd: 文件描述符的范围,比待检的最大文件描述符大1

Readfds:被读监控的文件描述符集

Writefds:被写监控的文件描述符集

Exceptfds:被异常监控的文件描述符集

Timeout:定时器

Timeout取不一样的值,该调用有不一样的表现:

Timeout值为0,无论是否有文件知足要求,都马上返回,无文件知足要求返回0,有文件知足要求返回一个正值。

Timeout为NULL,select将阻塞进程,直到某个文件知足要求

Timeout值为正整数,就是等待的最长时间,即select在timeout时间内阻塞进程。

Select调用返回时,返回值有以下状况:

1. 正常状况下返回知足要求的文件描述符个数;

2. 通过了timeout等待后仍无文件知足要求,返回值为0;

3. 若是select被某个信号中断,它将返回-1并设置errno为EINTR。

4. 若是出错,返回-1并设置相应的errno。

Select使用步骤

1. 设置要监控的文件

2. 调用Select开始监控

3. 判断文件是否发生变化

统提供了4个宏对描述符集进行操做:

#include<sys/select.h>

void FD_SET(int fd,fd_set *fdset) 宏FD_SET将文件描述符fd添加到文件描述符集fdset中;

void FD_CLR(int fd,fd_set *fdset) 宏FD_CLR从文件描述符集fdset中清除文件描述符fd;

void FD_ZERO(fd_set*fdset) 宏FD_ZERO清空文件描述符集fdset;

void FD_ISSET(intfd, fd_set *fdset) 在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件fd发生了变化。

FD_ZERO(&fds);//清空集合

sock1 =socket(......);

sock2 =socket(......);

bind(sock1,...);

bind(sock2,...);

listen(sock1,...);

listen(sock1,...);

FD_SET(sock1,&fds);//设置描述符

FD_SET(sock2,&fds);//设置描述符

maxfdp=(sock1>sock2?sock1:sock2)+ 1;

switch(select(maxfdp,&fds,NULL,NULL,&timeout))

case -1:exit(-1);break; //select错误,退出程序

case 0:break;

default:

if(FD_ISSET(sock1,&fds))//测试sock1是否可读

accpet(sock1,...)

相关文章
相关标签/搜索