Linux下学生信息管理系统设计报告文档格式.docx
《Linux下学生信息管理系统设计报告文档格式.docx》由会员分享,可在线阅读,更多相关《Linux下学生信息管理系统设计报告文档格式.docx(21页珍藏版)》请在冰豆网上搜索。
首先,建立网络的起因是网络中软硬件资源、运算能力和信息不均等,需要共享,从而造就拥有众多资源的主机提供服务,资源较少的客户请求服务这一非对等作用。
其次,网间进程通信完全是异步的,相互通信的进程间既不存在父子关系,又不共享内存缓冲区,因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户端/服务器模式的TCP/IP。
服务器方
要先启动,并根据请求提供相应服务:
1)打开一通信通道并告知本地主机,它愿意在某一公认地址上接收客户请求;
2)等待客户请求到达该端口;
3)接收到重复服务请求,处理该请求并发送应答信号。
接收到并发服务请求,要激活一新进程来处理这个客户请求(如UNIX系统中用fork、exec)。
新进程处理此客户请求,并不需要对其它请求作出应答。
服务完成后,关闭此新进程与客户的通信链路,并终止。
4)返回第二步,等待另一客户请求。
5)关闭服务器
客户方:
1)打开一通信通道,并连接到服务器所在主机的特定端口;
2)向服务器发服务请求报文,等待并接收应答;
继续提出请求......
3)
请求结束后关闭通信通道并终止。
3.Socket套接字的三种类型
流式套接字(SOCK_STREAM)
提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。
内设流量控制,避免数据流超限;
数据被看作是字节流,无长度限制。
文件传送协议(FTP)即使用流式套接字。
数据报式套接字(SOCK_DGRAM)
提供了一个无连接服务。
数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。
网络文件系统(NFS)使用数据报式套接字。
原始式套接字(SOCK_RAW)
该接口允许对较低层协议,如IP、ICMP直接访问。
常用于检验新的协议实现或访问现有服务中配置的新设备。
4.Socket工作流程
1)创建套接字──socket()
应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段,其调用格式如下:
SOCKET
PASCAL
FAR
socket(int
af,
int
type,
protocol);
该调用要接收三个参数:
af、type、protocol。
参数af指定通信发生的区域,UNIX系统支持的地址族有:
AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是网际网区域。
因此,地址族与协议族相同。
参数type
描述要建立的套接字的类型。
参数protocol说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。
根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。
因此,socket()系统调用实际上指定了相关五元组中的“协议”这一元。
2)指定本地地址──bind()
当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。
bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。
其调用格式如下:
bind(SOCKET
s,
const
struct
sockaddr
*
name,
namelen);
参数s是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
参数name
是赋给套接字s的本地地址(名字),其长度可变,结构随通信域的不同而不同。
namelen表明了name的长度。
如果没有错误发生,bind()返回0。
否则返回值SOCKET_ERROR。
3)建立套接字连接──connect()与accept()
Connect:
客户机端向服务器端发送出请求连接,它使用的目的端口号是服务器端用做监听的套接字使用保留的端口号,执行此项命令之后,客户机端进入到阻塞的状态,等待服务器的连接应答。
一旦接收来自服务器端的响应套接字的应答,客户机端就向服务器的响应套接字发送连接确认,这样,客户机端与服务器的TCP连接就建立起来了
Accept:
接收客户机端的请求,分为两种情况。
如果此时,监听套接字的请求缓冲区队列中有客户机端的连接请求在等待,就从中取出一个连接请求,并接收他。
具体过程是:
服务器端创建一个新的套接字,成为相应套接字,成为响应套接字。
系统赋予给这个响应套接字一个服务器端的自由端口号,并通过响应套接字向客户端发送连接应答,客户机端收到这个应答,按照TCP连接规范,向服务器端发送连接确认,并同时向服务器端发来数据,这就完成了TCP的三次握手的过程。
此后,就由服务器端的这个响应套接字专门负责与该客户机端的交换数据工作。
以上过程当然就同时腾出了一个监听套接字的请求缓冲单元,又可以接纳新的连接请求。
如果此时,监听套接字的请求缓冲区队列中没有任何客户机端的连接请求在等待,执行此命令就会使服务器端的进程处于阻塞等待的状态,使它时刻准备接收来自客户机端的连接请求。
服务器端采用这种方式能够同时为多个客户机服务
这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。
无连接的套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。
这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到的一个数据报,其目的端口未与任何套接字建立“连接”,便能判断该端靠纪纪可操作。
而accept()用于使服务器等待来自某客户进程的实际连接。
connect()的调用格式如下:
connect(SOCKET
namelen)参数s是欲建立连接的本地套接字描述符。
参数name指出说明对方套接字地址结
构的指针。
对方套接字地址长度由namelen说明。
如果没有错误发生,connect()返回0。
在面向连接
的协议中,该调用导致本地系统和外部系统之间连接实际建立。
由于地址族总被包含在套接字地址结构的前两个字节中,并通过socket()调用与某
个协议族相关。
因此bind()和connect()无须协议作为参数。
accept()的调用格式如下:
accept(SOCKET
FAR*
addr,
addrlen);
参数s为本地套接字描述符,在用做accept()调用的参数前应该先调用过listen()。
addr
指向客户方套接字地址结构的指针,用来接收连接实体的地址。
addr的确切格式由套接字创建时建立的地址族决定。
addrlen
为客户方套接字地址的长度(字节数)。
如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。
否则返回值INVALID_SOCKET。
accept()用于面向连接服务器。
参数addr和addrlen存放客户方的地址信息。
调用前,参数addr
指向一个初始值为空的地址结构,而addrlen
的初始值为0;
调用accept()后,服务器等待从编号为s的套接字上接受客户连接请求,而连接请求是由客户方的connect()调用发出的。
当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr
和addrlen,并创建一个与s有相同特性的新套接字号。
新的套接字可用于处理服务器并发请求。
四个套接字系统调用,socket()、bind()、connect()、accept(),可以完成一个完全五元相关的建立。
socket()指定五元组中的协议元,它的用法与是否为客户或服务器、是否面向连接无关。
bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关:
在服务器方,无论是否面向连接,均要调用bind();
钥纪纪户方,若采用面向连接,则可以不调用bind(),而通过connect()自动完成。
若采用无连接,客户方必须使用bind()以获得一个唯一的地址。
4)监听连接──listen()
此调用用于面向连接服务器,表明它愿意接收连接。
listen()需在accept()之前调用,其调用格式如下:
listen(SOCKET
backlog);
参数s标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。
backlog表示请求连接队列的最大长度,用于限制排队请求的个数,目前允许的最大值为5。
如果没有错误发生,listen()返回0。
否则它返回SOCKET_ERROR。
listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必须的连接,并建立长度为backlog的请求连接队列。
调用listen()是服务器接收一个连接请求的四个步骤中的第三步。
它在调用socket()分配一个流套接字,且调用bind()给s赋于一个名字之后调用,而且一定要在accept()之前调用。
5)数据传输──send()与recv()
当一个连接建立以后,就可以传输数据了。
常用的系统调用有send()和recv()。
send()调用用于钥纪纪数s指定的已连接的数据报或流套接字上发送输出数据,格式如下:
send(SOCKET
char
*buf,
len,
flags);
参数s为已连接的本地套接字描述符。
buf
指向存有发送数据的缓冲区的指针,其长度由len
指定。
flags
指定传输控制方式,如是否发送带外数据等。
如果没有错误发生,send()返回总共发送的字节数。
recv()调用用于钥纪纪数s指定的已连接的数据报或流套接字上接收输入数据,格式如下:
recv(SOCKET
参数s
为已连接的套接字描述符。
buf指向接收输入数据缓冲区的指针,其长度由len
指定传输控制方式,如是否接收带外数据等。
如果没有错误发生,recv()返回总共接收的字节数。
如果连接被关闭,返回0。
6)输入/输出多路复用──select()
select()调用用来检测一个或多个套接字的状态。
对每一个套接字来说,这个调用可以请求读、写或错误状态方面的信息。
请求给定状态的套接字集合由一个fd_set结构指示。
在返回时,此结构被更新,以反映那些满足特定条件的套接字的子集,同时,
select()调用返回满足条件的套接字的数目,其调用格式如下:
select(int
nfds,
fd_set
readfds,
writefds,
exceptfds,
timeval
timeout);
参数nfds指明被检查的套接字描述符的值域,此变量一般被忽略。
参数readfds指向要做读检测的套接字描述符集合的指针,调用者希望从中读取数据。
参数writefds
指向要做写检测的套接字描述符集合的指针。
exceptfds指向要检测是否出错的套接字描述符集合的指针。
timeout指向select()函数等待的最大时间,如果设为NULL则为阻塞操作。
select()返回包含在fd_set结构中已准备好的套接字描述符的总数目,或者是发生错误则返回SOCKET_ERROR。
7)关闭套接字──closesocket()
closesocket()关闭套接字s,并释放分配给该套接字的资源;
如果s涉及一个打开的TCP连接,则该连接被释放。
closesocket()的调用格式如下:
BOOL
closesocket(SOCKET
s);
参数s待关闭的套接字描述符。
如果没有错误发生,closesocket()返回0。
5.Socket的实现
服务器端:
在服务器端,主要是启动Socket和监听线程。
#defineDEFAULT_PORT
2000voidCServerDlg:
:
OnStart()
{
sockaddr_inlocal;
DWORDdwThreadID=0;
local.sin_family=AF_INET;
//设置的端口为DEFAULT_PORT。
local.sin_port=htons(DEFAULT_PORT);
//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
local.sin_addr.S_un.S_addr=INADDR_ANY;
//初始化Socket
m_Listening=socket(AF_INET,SOCK_STREAM,0);
if(m_Listening==INVALID_SOCKET)
{
return;
}
//将本地地址绑定到所创建的套接字上
if(bind(m_Listening,(LPSOCKADDR)&
local,sizeof(local))==SOCKET_ERROR)
{
closesocket(m_Listening);
}
//创建监听线程,这样也能响应界面上操作。
m_hListenThread=:
CreateThread(NULL,0,ListenThread,this,0,
&
dwThreadID);
m_StartBtn.EnableWindow(FALSE);
m_StopBtn.EnableWindow(TRUE);
}
//监听线程函数:
WORDWINAPICServerDlg:
ListenThread(LPVOIDlpparam)
CServerDlg*pDlg=(CServerDlg*)lpparam;
if(pDlg==NULL)
return0;
SOCKETListening=pDlg->
m_Listening;
//开始监听是否有客户端连接。
if(listen(Listening,40)==SOCKET_ERROR)
{
return0;
}
charszBuf[MAX_PATH];
//初始化
memset(szBuf,0,MAX_PATH);
while
(1)
SOCKETConnectSocket;
sockaddr_inClientAddr;
intnLen=sizeof(sockaddr);
//阻塞直到有客户端连接,不然多浪费CPU资源。
ConnectSocket=accept(Listening,(sockaddr*)&
ClientAddr,&
nLen);
//都到客户端的IP地址。
char*pAddrname=inet_ntoa(ClientAddr.sin_addr);
pDlg->
Receive(ConnectSocket,szBuf,100);
//界面上显示请求数据。
SetRequestText(szBuf);
strcat(szBuf,"
:
我是老猫,收到("
);
strcat(szBuf,pAddrname);
strcat(szBuf,"
)"
//向客户端发送回应数据
Send(ConnectSocket,szBuf,100);
}
服务器端一直在监听是否有客户端连接,如有连接,处理客户端的请求,给出回应,然后继续监听。
客户端:
客户端的发送函数:
#defineDEFAULT_PORT2000
voidCClientDlg:
OnSend()
{
DWORDdwIP=0;
TCHARszText[MAX_PATH];
memset(szText,0,MAX_PATH);
m_IP.GetWindowText(szText,MAX_PATH);
//把字符串形式的IP地址转成IN_ADDR结构需要的形式。
dwIP=inet_addr(szText);
m_RequestEdit.GetWindowText(szText,MAX_PATH);
ockaddr_inlocal;
SOCKETsocketTmp;
//必须是AF_INET,表示该socket在Internet域中进行通信
//端口号
//服务器的IP地址。
local.sin_addr.S_un.S_addr=dwIP;
//初始化Socket
socketTmp=socket(AF_INET,SOCK_STREAM,0);
//连接服务器
if(connect(socketTmp,(LPSOCKADDR)&
local,sizeof(local))<
0)
closesocket(socketTmp);
MessageBox("
连接服务器失败。
"
return;
}
//发送请求,为简单只发100字节,在服务器端也规定100字节。
Send(socketTmp,szText,100);
//读取服务器端返回的数据。
//接收服务器端的回应。
Receive(socketTmp,szText,100);
CHARszMessage[MAX_PATH];
memset(szMessage,0,MAX_PATH);
strcat(szMessage,szText);
//界面上显示回应数据。
m_ReplyBtn.SetWindowText(szMessage);
closesocket(socketTmp);
}
客户端就一个函数完成了一次通信。
6.系统部分模块代码
1)添加学生
voidadd_student(type_stu**head,intclient_fd)//
charbuf[BUFSIZ];
inti=0,j=0,k=0;
//creattailnode
if(*head==NULL)
pthread_mutex_lock(&
mut);
*head=(type_stu*)malloc(sizeof(type_stu));
pthread_mutex_unlock(&
if(*head==NULL)
return;
memset(*head,0,sizeof(type_stu));
(*head)->
next=NULL;
send(client_fd,"
Inputnumber,name,sex,age,password:
\n"
strlen("
)+1,0);
recv(client_fd,buf,BUFSIZ,0);
number=atoi(buf);
for(i=0;
i<
BUFSIZ;
i++)
{
if(buf[i]=='
'
)
{
j++;
if(j==1)
while