??xml version="1.0" encoding="utf-8" standalone="yes"?> 具体的实C是按这个流E图来做的,q里重点是服务端的实现?/p>
在接收客L发来数据的地方要做成d@环,如果需要断开q接Q则由客h发送特定的消息然后q行处理。还有需要注意的是上面的HostIp是本机的IP地址QPORT是套接字需要绑定的端口?/p>
]]>
]]>
非阻塞模式:执行I/O操作ӞWinsock函数会返回ƈ交出控制权。这U模式?/span> h比较复杂Q因为函数在没有q行完成p行返回,会不断地q回 WSAEWOULDBLOCK错误。但功能强大?/span>
]]>
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
ofstream logfile("LogFile.txt");
//Initialize winsock
WSADATA wsaData;
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData);
if(iResult != NO_ERROR)
{
logfile<<"Error at WSAStartup() ";
logfile.close();
return 1;
}
else
logfile<<"Initialize WSAStartup OK!";
// Create a socket.
SOCKET serverSocket;
serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(serverSocket == INVALID_SOCKET)
{
logfile<<"Error at socket():"<<WSAGetLastError()<<endl;;
logfile.close();
WSACleanup();
return 1;
}
else
{
logfile<<"Create socket OK!";
}
// Bind the socket.
sockaddr_in service;
service.sin_family=AF_INET;
service.sin_addr.s_addr=inet_addr(HostIp.c_str());
service.sin_port=htons(PORT);
if (bind(serverSocket,(SOCKADDR*)&service,sizeof(service))==SOCKET_ERROR)
{
logfile<<"bind() failed"<<GetLastError()<<endl;
closesocket(serverSocket);
logfile.close();
return 1;
}
else
{
logfile<<"Binding OK!"<<endl;
}
// Listen on the socket.
if(listen(serverSocket,1)==SOCKET_ERROR)
{
logfile<<"Error listening on socket"<<GetLastError()<<endl;
logfile.close();
}
else
{
logfile<<"Listening..."<<endl;
}
// Accept connections.
SOCKET clientSocket;
sockaddr_in clientAddr;
int clientAddrLen=sizeof(clientAddr);
while(true)
{
clientSocket = SOCKET_ERROR;
while(clientSocket==SOCKET_ERROR)
{
clientSocket=accept(serverSocket,(struct sockaddr*)&clientAddr,&clientAddrLen);
}
ReceiveData(clientSocket);
}
closesocket(serverSocket);
closesocket(clientSocket);
return 0;
}
//Receive the data
void ReceiveData(SOCKET& clientSocket)
{
int bytesSent;
int bytesRecv=SOCKET_ERROR;
string sendbuf="";
char recvbuf[32]="";
while(bytesRecv ==SOCKET_ERROR)
{
bytesRecv=recv(clientSocket,recvbuf,32,0);
sendbuf="Received: "+(string)recvbuf;
bytesSent=send(clientSocket,sendbuf.c_str(),(unsigned int)(sendbuf.size()),0);
bytesRecv=SOCKET_ERROR;
memset(recvbuf,'\0',32);
一般方法是:建立两个q接句柄队列,I闲的等待用的队列和正在用的队列.
当要查询时先从空闲队列中获取一个句?插入到正在用的队列,再用q个句柄做数据库操作,完毕后一定要从用队列中删除,再插入到I闲队列.
代码如下Q?br>MySQLMan.h
// MySQLMan.h: interface for the CMySQLMan class.
//
//////////////////////////////////////////////////////////////////////
#include <mysql.h>
#pragma comment(lib,"libmySQL.lib")
#include <list>
typedef std::list<MYSQL *> CONNECTION_HANDLE_LIST;
typedef std::list<MYSQL *>::iterator ITER_CONNECTION_HANDLE_LIST;
#define CONNECTION_NUM 10 //同时打开的连接数
class CMySQLMan
{
public:
CMySQLMan();
CMySQLMan(const char *host, const char *user, const char *password, const char *db, unsigned int port=3306);
virtual ~CMySQLMan();
public:
bool ConnectDB(); //q接数据?br> MYSQL_RES* SelectRecord(const char *szSql); //选择记录Q返回结果集
bool SelectDB(const char *szDB); //选择数据?br> bool UpdateRecord(const char *szSql); //更新记录
bool InsertRecord(const char *szSql); //插入记录
bool DelRecord(const char *szSql); //删除记录
BOOL IsEnd(MYSQL_RES *myquery); //是否最?br> void SeekData(MYSQL_RES *myquery, int offset); //查找指定数据
void FreeRecord(MYSQL_RES *myquery); //释放l果?br> unsigned int GetFieldNum(MYSQL_RES *myquery); //得到字段?br> MYSQL_ROW GetRecord(MYSQL_RES *myquery); //得到l果Q一个记录)
my_ulonglong GetRowNum(MYSQL_RES *myquery); //得到记录?br> char* OutErrors(MYSQL* pMySql); //输出错误信息
char* GetState(); //服务器状?br> char* GetServerInfo(); //服务器信?br> int GetProtocolInfo(); //协议信息
char* GetHostInfo(); //L信息
char* GetClientInfo(); //客户Z?br> char* GetFieldName(MYSQL_RES *myquery, int FieldNum); //字段?/p>
bool LockTable(const char *TableName, const char *Priority); //对特定表加锁
bool UnlockTable(); //解锁
bool SetCharset();
//int CreateDB(char *db); //创徏数据库,q回错误信息
//int DropDB(char *db); //删除数据?q回错误信息
MYSQL* GetIdleMySql(); //提取一个空闲句柄供使用
void SetIdleMysql(MYSQL* pMySql); //从用队列中释放一个用完毕的句柄Q插入到I闲队列
public:
//MYSQL m_mysql; //数据库连接句?br> MYSQL_ROW m_row; //记录?单行)
MYSQL_FIELD *m_field; //字段信息Q结构体Q?/p>
//创徏两个队列
CONNECTION_HANDLE_LIST m_lsBusyList; //正在使用的连接句?br> CONNECTION_HANDLE_LIST m_lsIdleList; //未用的q接句柄
CRITICAL_SECTION m_csList;
public:
char m_host[20]; //L
char m_user[20]; //用户?br> char m_password[20]; //密码
char m_db[20]; //数据库名
unsigned int m_port; //端口
};
MySQLMan.cpp
// MySQLMan.cpp: implementation of the MySQLMan class.
//
//////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "MySQLMan.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CMySQLMan::CMySQLMan()
{
}
CMySQLMan::CMySQLMan(const char *host, const char *user, const char *password, const char *db, unsigned int port/* =3306 */)
{
strcpy(m_host, host);
strcpy(m_user, user);
strcpy(m_password, password);
strcpy(m_db, db);
m_port = port;
InitializeCriticalSection(&m_csList);
}
CMySQLMan::~CMySQLMan()
{
for (ITER_CONNECTION_HANDLE_LIST iter=m_lsBusyList.begin(); iter != m_lsBusyList.end(); iter++)
{
mysql_close((*iter));
}
for (ITER_CONNECTION_HANDLE_LIST iter=m_lsIdleList.begin(); iter != m_lsIdleList.end(); iter++)
{
mysql_close((*iter));
}
DeleteCriticalSection(&m_csList);
}
bool CMySQLMan::ConnectDB()
{
//同时打开CONNECTION_NUM个连?br> try
{
for (int i=0; i<CONNECTION_NUM; ++i)
{
MYSQL *pMySql = mysql_init((MYSQL*)NULL);
if (pMySql != NULL)
{
if (!mysql_real_connect(pMySql,m_host,m_user,m_password,m_db,m_port,NULL,0))
{
OutErrors(pMySql);
return false;
}
m_lsIdleList.push_back(pMySql);
}
}
}
catch (...)
{
return false;
}
return true;
}
MYSQL* CMySQLMan::GetIdleMySql()
{
MYSQL* pMySql = NULL;
EnterCriticalSection(&m_csList);
if (m_lsIdleList.size() > 0)
{
pMySql = m_lsIdleList.front();
m_lsIdleList.pop_front();
m_lsBusyList.push_back(pMySql);
}
else
{
pMySql = NULL;
}
LeaveCriticalSection(&m_csList);
return pMySql;
}
void CMySQLMan::SetIdleMysql(MYSQL* pMySql)
{
EnterCriticalSection(&m_csList);
m_lsBusyList.remove(pMySql);
m_lsIdleList.push_back(pMySql);
LeaveCriticalSection(&m_csList);
}
MYSQL_RES* CMySQLMan::SelectRecord(const char *szSql)
{
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return NULL;
}
if(mysql_query(pMySql,szSql))
return NULL;
MYSQL_RES *myquery = NULL;
myquery = mysql_store_result(pMySql);
SetIdleMysql(pMySql);
return myquery;
}
bool CMySQLMan::InsertRecord(const char *szSql)
{
bool bRet = false;
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return false;
}
if(mysql_query(pMySql,szSql))
{
bRet = true;
}
SetIdleMysql(pMySql);
return bRet;
}
bool CMySQLMan::UpdateRecord(const char *szSql)
{
bool bRet = false;
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return false;
}
if(mysql_query(pMySql,szSql))
{
bRet = true;
}
SetIdleMysql(pMySql);
return bRet;
}
bool CMySQLMan::DelRecord(const char *szSql)
{
bool bRet = false;
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return false;
}
if(mysql_query(pMySql,szSql))
{
bRet = true;
}
SetIdleMysql(pMySql);
return bRet;
}
bool CMySQLMan::SelectDB(const char *szDB)
{
bool bRet = false;
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return false;
}
if (mysql_select_db(pMySql,szDB))
bRet = false;
else
bRet = true;
SetIdleMysql(pMySql);
return bRet;
}
my_ulonglong CMySQLMan::GetRowNum(MYSQL_RES *myquery)
{
return mysql_num_rows(myquery);
}
MYSQL_ROW CMySQLMan::GetRecord(MYSQL_RES *myquery)
{
m_row = mysql_fetch_row(myquery);
return m_row;
}
unsigned int CMySQLMan::GetFieldNum(MYSQL_RES *myquery)
{
return mysql_num_fields(myquery);
}
void CMySQLMan::FreeRecord(MYSQL_RES *myquery)
{
mysql_free_result(myquery);
}
//int CMySQLMan::CreateDB(char *db)
//{
// return mysql_create_db(&m_mysql,db);
//}
void CMySQLMan::SeekData(MYSQL_RES *myquery, int offset)
{
mysql_data_seek(myquery,offset);
}
char * CMySQLMan::OutErrors(MYSQL *pMySql)
{
return const_cast<char *>(mysql_error(pMySql));
}
BOOL CMySQLMan::IsEnd(MYSQL_RES *myquery)
{
return mysql_eof(myquery);
}
char* CMySQLMan::GetFieldName(MYSQL_RES *myquery, int FieldNum)
{
m_field = mysql_fetch_field_direct(myquery, FieldNum);
return m_field->name;
}
char * CMySQLMan::GetClientInfo()
{
return const_cast<char *>(mysql_get_client_info());
}
char* CMySQLMan::GetHostInfo()
{
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return NULL;
}
return const_cast<char *>(mysql_get_host_info(pMySql));
}
int CMySQLMan::GetProtocolInfo()
{
int iRet = 0;
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return NULL;
}
iRet = mysql_get_proto_info(pMySql);
SetIdleMysql(pMySql);
return iRet;
}
char* CMySQLMan::GetServerInfo()
{
static char szRet[1024];
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return NULL;
}
_tcscpy(szRet, const_cast<char *>(mysql_get_server_info(pMySql)));
SetIdleMysql(pMySql);
return szRet;
}
char* CMySQLMan::GetState()
{
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return NULL;
}
static char szRet[1024];
_tcscpy(szRet,const_cast<char *>(mysql_stat(pMySql)));
SetIdleMysql(pMySql);
return szRet;
}
bool CMySQLMan::SetCharset()
{
bool bRet = false;
char szSql[50];
strcpy(szSql, "set names gb2312");
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return false;
}
if (mysql_query(pMySql, szSql))
bRet = true;
SetIdleMysql(pMySql);
return bRet;
}
//LOCK TABLES tbl1 READ, tbl2 WRITE
bool CMySQLMan::LockTable(const char *TableName, const char *Priority)
{
bool bRet = false;
char szSql[50];
sprintf(szSql, "LOCK TABLES %s %s", TableName, Priority);
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return false;
}
if (mysql_query(pMySql, szSql))
bRet = true;
SetIdleMysql(pMySql);
return bRet;
}
bool CMySQLMan::UnlockTable()
{
bool bRet = false;
MYSQL *pMySql = GetIdleMySql();
if (pMySql == NULL)
{
return false;
}
if(mysql_query(pMySql,"UNLOCK TABLES"))
bRet = true;
SetIdleMysql(pMySql);
return bRet;
}
下面是一个检验本机字节序的简便方法:
//判断本机的字节序
//q回true表ؓ段序。返回false表示为大D序
bool am_little_endian ()
{
unsigned short i=1;
return (int)*((char *)(&i)) ? true : false;
}
int main()
{
if(am_little_endian())
{
printf("本机字节序ؓ段?\n");
}
else
{
printf("本机字节序ؓ大段?\n");
}
return 0;
}
Socket接口?span>TCP/IP|络?span>APIQ?span>Socket接口定义了许多函数或例程Q程序员可以用它们来开?span>TCP/IP|络上的应用E序。要?span>Internet上的TCP/IP|络~程Q必ȝ?span>Socket接口?span>
Socket接口设计者最先是接口放?span>Unix操作pȝ里面的。如果了?span>Unixpȝ的输入和输出的话Q就很容易了?span>Socket了。网l的Socket数据传输是一U特D的I/OQ?span>Socket也是一U文件描q符?span>Socket也具有一个类g打开文g的函数调?span>Socket()Q该函数q回一个整型的Socket描述W,随后的连接徏立、数据传输等操作都是通过?span>Socket实现的。常用的Socketcd有两U:式SocketQ?span>SOCK_STREAMQ和数据报式SocketQ?span>SOCK_DGRAMQ。流式是一U面向连接的SocketQ针对于面向q接?span>TCP服务应用Q数据报?span>Socket是一U无q接?span>SocketQ对应于无连接的UDP服务应用?span>
Socket建立
Z建立SocketQ程序可以调?span>socket函数Q该函数q回一个类g文g描述W的句柄?span>socket函数原型为:
int socket(int domain, int type, int protocol);
domain指明所使用?span>协议族,通常?span>AF_INETQ表C?span>互联|?/span>协议族(TCP/IP协议族)Q?span>type参数指定socket的类型:SOCK_STREAM ?span>SOCK_DGRAMQ?span>Socket接口q定义了原始SocketQ?span>SOCK_RAWQ,允许E序使用低层协议Q?span>protocol通常赋?span>"0"Q表C根?span>type来自动选择协议Q?span>socket()调用q回一个整?span>socket描述W,你可以在后面的调用用它?/span>
Socket描述W是一个指向内部数据结构的指针Q它指向描述W表入口。调?span>Socket函数Ӟsocket执行体将建立一?span>SocketQ实际上"建立一?span>Socket"意味着Z?span>Socket数据l构分配存储I间?span>Socket执行体ؓ你管理描q符表?/span>
两个|络E序之间的一个网l连接包括五U信息:通信协议、本地协议地址、本C机端口、远端主机地址和远端协议端口?span>Socket数据l构中包含这五种信息?span>
Socket配置
通过socket函数调用q回一?span>socket描述W后Q在使用socketq行|络传输以前Q必配|该socket。面向连接的socket客户端通过调用connect函数?span>socket数据l构中保存本地和q端信息。无q接socket的客L和服务端以及面向q接socket的服务端通过调用bind函数来配|本C息?span>
bind函数?span>socket与本Z的一个端口相兌Q随后你可以在该端口监听服务请求?span>bind函数原型为:
int bind(int sockfd,struct sockaddr *my_addr, int addrlen); my_addr是一个指向包含有本机IP地址及端口号{信息的sockaddrcd的指针; addrlen常被讄?span>sizeof(struct sockaddr)?span> |
q个l构更方便用?span>sin_zero用来?span>sockaddr_inl构填充Cstruct sockaddr同样的长度,可以?span>bzero()?span>memset()函数其|ؓ零。指?span>sockaddr_in 的指针和指向sockaddr的指针可以相互{换,q意味着如果一个函数所需参数cd?span>sockaddrӞ你可以在函数调用的时候将一个指?span>sockaddr_in的指针{换ؓ指向sockaddr的指针;或者相反?span>
使用bind函数Ӟ可以用下面的赋值实现自动获得本?span>IP地址和随取一个没有被占用的端口号Q?span>
my_addr.sin_port = 0; /* pȝ随机选择一个未被用的端口?span> */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */
通过?span>my_addr.sin_port|ؓ0Q函C自动Z选择一个未占用的端口来使用。同P通过?span>my_addr.sin_addr.s_addr|ؓINADDR_ANYQ系l会自动填入本机IP地址?/span>
注意在?span>bind函数是需要将sin_port?span>sin_addr转换成ؓ|络字节优先序Q?span>sin_family则不需要{换?/span>
计算机数?span>存储有两U字节优先顺序:高位字节优先和低位字节优先?span>Internet上数据以高位字节优先序在网l上传输Q所以对于在内部是以低位字节优先方式存储数据的机器,?span>Internet上传输数据时需要进行{换,否则׃出现数据不一致?/span>
下面是几个字节顺序{换函敎ͼ
·htonl()Q把32位gL字节序{换成|络字节?span> |
bind()函数在成功被调用时返?span>0Q出现错误时q回"-1"q将errno|ؓ相应的错误号。需要注意的是,在调?span>bind函数时一般不要将端口L为小?span>1024的|因ؓ1?span>1024是保留端口号Q你可以选择大于1024中的M一个没有被占用的端口号?/span>
q接建立
面向q接的客L序?span>connect函数来配|?span>socketq与q端服务?/span>建立一?span>TCPq接Q其函数原型为:
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd?span>socket函数q回?span>socket描述W;serv_addr是包含远端主?span>IP地址和端口的指针Q?span>addrlen是远端地址l构的长?span>(sizeof(struct sockaddr))?span>connect函数在出现错误时q回-1Qƈ且设|?span>errno为相应的错误码。进行客LE序设计无须调用bind()Q因U情况下只需知道目的机器?span>IP地址Q而客户通过哪个端口?span>服务?/span>建立q接q不需要关心,socket执行体ؓ你的E序自动选择一个未被占用的端口Qƈ通知你的E序数据什么时候到打断口?/span>
connect函数启动和远端主机的直接q接。只有面向连接的客户E序使用socket时才需要将?span>socket与远端主机相q。无q接协议从不建立直接q接。面向连接的服务器也从不启动一个连接,它只是被动的在协议端口监听客Lh?span>
listen函数?span>socket处于被动的监听模式,qؓ?span>socket建立一个输入数据队列,到辄服务h保存在此队列中,直到E序处理它们?/span>
int listen(int sockfdQ?span> int backlog);
sockfd?span>Socketpȝ调用q回?span>socket 描述W;backlog指定在请求队列中允许的最大请求数Q进入的q接h在队列中等?span>accept()它们Q参考下文)?span>backlog寚w列中{待服务的请求的数目q行了限Ӟ大多数系l缺省gؓ20。如果一个服务请求到来时Q输入队列已满,?span>socket拒l连接请求,客户收C个出错信息?/span>
当出现错误时listen函数q回-1Qƈ|相应的errno错误码?/span>
accept()函数让服务器接收客户的连接请求。在建立好输入队列后Q服务器p?span>accept函数Q然后睡眠ƈ{待客户的连接请求?/span>
int accept(int sockfd, void *addr, int *addrlen);
sockfd是被监听?span>socket描述W,addr通常是一个指?span>sockaddr_in变量的指针,该变量用来存放提接请求服务的L的信息(某台L从某个端口发hQ;addrlen通常Z个指向gؓsizeof(struct sockaddr_in)的整型指针变量。出现错误时accept函数q回-1q置相应?span>errno倹{?/span>
首先Q当accept函数监视?span>socket收到q接hӞsocket执行体将建立一个新?span>socketQ执行体这个新socket和请求连接进E的地址联系hQ收到服务请求的初始socket仍可以l在以前?span> socket上监听,同时可以在新?span>socket描述W上q行数据传输操作?span>
数据传输
send()?span>recv()q两个函数用于面向连接的socket上进行数据传输?span>
send()函数原型为:
int send(int sockfd, const void *msg, int len, int flags);
sockfd是你想用来传输数据的socket描述W;msg是一个指向要发送数据的指针Q?span>len是以字节为单位的数据的长度;flags一般情况下|ؓ0Q关于该参数的用法可参照man手册Q?span>
send()函数q回实际上发送出的字节数Q可能会于你希望发送的数据。在E序中应该将send()的返回gƲ发送的字节数进行比较。当send()q回glen不匹配时Q应该对q种情况q行处理?span>
char *msg = "Hello!";
int len, bytes_sent;
……
len = strlen(msg);
bytes_sent = send(sockfd, msg,len,0);
……
recv()函数原型为:
int recv(int sockfd,void *buf,int len,unsigned int flags);
sockfd是接受数据的socket描述W;buf 是存放接收数据的~冲区;len是缓冲的长度?span>Flags也被|ؓ0?span>recv()q回实际上接收的字节敎ͼ当出现错误时Q返?span>-1q置相应?span>errno倹{?span>
sendto()?span>recvfrom()用于在无q接的数据报socket方式下进行数据传输。由于本?span>socketq没有与q端机器建立q接Q所以在发送数据时应指明目的地址?span>
sendto()函数原型为:
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
该函数比send()函数多了两个参数Q?span>to表示目地机的IP地址和端口号信息Q?span>tolen常常被赋gؓsizeof (struct sockaddr)?span>sendto 函数也返回实际发送的数据字节长度或在出现发送错误时q回-1?span>
recvfrom()函数原型为:
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
from是一?span>struct sockaddrcd的变量,该变量保存源机的IP地址及端口号?span>fromlen常置?span>sizeof (struct sockaddr)。当recvfrom()q回Ӟfromlen包含实际存入from中的数据字节数?span>recvfrom()函数q回接收到的字节数或当出现错误时q回-1Qƈ|相应的errno?span>
如果你对数据?span>socket调用?span>connect()函数Ӟ你也可以利用send()?span>recv()q行数据传输Q但?span>socket仍然是数据报socketQƈ且利用传输层?span>UDP服务。但在发送或接收数据报时Q内怼自动Z加上目地和源地址信息?span>
l束传输
当所有的数据操作l束以后Q你可以调用close()函数来释放该socketQ从而停止在?span>socket上的M数据操作Q?span>
close(sockfd);
你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输l进行。如你可以关闭某socket的写操作而允许l在?span>socket上接受数据,直至d所有数据?span>
int shutdown(int sockfd,int how);
sockfd是需要关闭的socket的描q符。参?span> how允许?span>shutdown操作选择以下几种方式Q?span>
·0-------不允许l接收数?span>
·1-------不允许l发送数?span>
·2-------不允许l发送和接收数据Q?span>
·均ؓ不允许则调用close()
shutdown在操作成功时q回0Q在出现错误时返?span>-1q置相应errno?span>
面向q接?span>Socket实例
代码实例中的服务器通过socketq接向客L发送字W串"Hello, you are connected!"。只要在服务器上q行该服务器软gQ在客户端运行客戯YӞ客户端就会收到该字符丌Ӏ?span>
该服务器软g代码如下Q?span>
#include |
服务器的工作程是这LQ首先调?span>socket函数创徏一?span>SocketQ然后调?span>bind函数其与本机地址以及一个本地端口号l定Q然后调?span>listen在相应的socket上监听,?span>accpet接收C个连接服务请求时Q将生成一个新?span>socket。服务器昄该客h?span>IP地址Qƈ通过新的socket向客L发送字W串"HelloQ?span>you are connected!"。最后关闭该socket?/span>
代码实例中的fork()函数生成一个子q程来处理数据传输部分,fork()语句对于子进E返回的gؓ0。所以包?span>fork函数?span>if语句是子q程代码部分Q它?span>if语句后面的父q程代码部分是ƈ发执行的?span>
客户端程序代码如下:
#include |
客户端程序首先通过服务器域名获得服务器?span>IP地址Q然后创Z?span>socketQ调?span>connect函数与服务器建立q接Q连接成功之后接收从服务器发送过来的数据Q最后关?span>socket?/span>
函数gethostbyname()是完成域名{换的。由?span>IP地址难以记忆和读写,所以ؓ了方便,Z常常用域名来表示LQ这需要进行域名和IP地址的{换。函数原型ؓQ?/span>
struct hostent *gethostbyname(const char *name); |
?span> gethostname()调用成功Ӟq回指向struct hosten的指针,当调用失败时q回-1。当调用gethostbynameӞ你不能?span>perror()函数来输出错误信息,而应该?span>herror()函数来输出?span>
无连接的客户/服务器程序的在原理上和连接的客户/服务器是一LQ两者的区别在于无连接的客户/服务器中的客户一般不需要徏立连接,而且在发送接收数据时Q需要指定远端机的地址?span>
d和非d
d函数在完成其指定的Q务以前不允许E序调用另一个函数。例如,E序执行一个读数据的函数调用时Q在此函数完成读操作以前不会执行下一E序语句。当服务器运行到accept语句Ӟ而没有客戯接服务请求到来,服务器就会停止在accept语句上等待连接服务请求的到来。这U情늧为阻塞(blockingQ。而非d操作则可以立卛_成。比如,如果你希望服务器仅仅注意查是否有客户在等待连接,有就接受q接Q否则就l箋做其他事情,则可以通过?span>Socket讄为非d方式来实现。非dsocket在没有客户在{待时就?span>accept调用立即q回?span>
#include
#include
……
sockfd = socket(AF_INET,SOCK_STREAM,0);
fcntl(sockfd,F_SETFL,O_NONBLOCK)Q?span>
……
通过讄socket为非d方式Q可以实?span>"轮询"若干Socket。当企图从一个没有数据等待处理的非阻?span>Socketd数据Ӟ函数立卌回,q回gؓ-1Qƈ|?span>errnogؓEWOULDBLOCK。但是这U?span>"轮询"会CPU处于忙等待方式,从而降低性能Q浪费系l资源。而调?span>select()会有效地解决q个问题Q它允许你把q程本n挂v来,而同时ɾpȝ内核监听所要求的一l文件描q符的Q何活动,只要认在Q何被监控的文件描q符上出现活动,select()调用返回指C文g描述W已准备好的信息Q从而实CE选出随机的变化,而不必由q程本n对输入进行测试而浪?span>CPU开销?span>select函数原型?span>:
int select(int numfds,fd_set *readfds,fd_set *writefdsQ?span>
fd_set *exceptfds,struct timeval *timeout);
其中readfds?span>writefds?span>exceptfds分别是被select()监视的读、写和异常处理的文g描述W集合。如果你希望定是否可以从标准输入和某个socket描述W读取数据,你只需要将标准输入的文件描q符0和相应的sockdtfd加入?span>readfds集合中;numfds的值是需要检查的L最高的文g描述W加1Q这个例子中numfds的值应?span>sockfd+1Q当selectq回Ӟreadfds被修改Q指C某个文件描q符已经准备被读取,你可以通过FD_ISSSET()来测试。ؓ了实?span>fd_set中对应的文g描述W的讄、复位和试Q它提供了一l宏Q?span>
FD_ZERO(fd_set *set)----清除一个文件描q符集;
FD_SET(int fd,fd_set *set)----一个文件描q符加入文g描述W集中;
FD_CLR(int fd,fd_set *set)----一个文件描q符从文件描q符集中清除Q?span>
FD_ISSET(int fd,fd_set *set)----试判断是否文件描q符被置位?span>
Timeout参数是一个指?span>struct timevalcd的指针,它可以select()在等?span>timeout长时间后没有文g描述W准备好卌回?span>struct timeval数据l构为:
struct timeval {
int tv_sec; /* seconds */
int tv_usec; /* microseconds */ };
POP3客户端实?span>
下面的代码实例基?span>POP3的客户协议,与邮件服务器q接q取回指定用户帐L邮g。与邮g服务器交互的命o存储在字W串数组POPMessage中,E序通过一?span>do-while循环依次发送这些命令?span>
#include main(int argc, char *argv[]){ if((host=gethostbyname("your.server"))==NULL) { do { iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0); iMsg++; close(sockfd); |