MSDN:socket function
原始套接字IP
流式套接字TCP
数据包套接字UDP
一.套接字I/O模式
阻塞模式(默认)和非阻塞模式ioctlsocket函数可设置
二.套接字函数
1.WSAStartup
WSAStartup函数用于初始化wa2_32.dll动态链接库。int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
wVersionRequested:调用者使用的Windows Socket的版本
lpWSAData:一个WSADATA结构指针
typedef struct WSAData {
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
} WSADATA, FAR * LPWSADATA;
wVersion:调用者使用的ws_32.dll动态链接库的版本号
wHighVersion:ws_32.dll支持的最高版本,通常与wVersion相同
szDescription:套接字描述信息,无实际意义
szSystemStatus:系统的配置或状态信息,无实际意义
iMaxSockets:最多可以打开多少个套接字,在套接字版本2或以后版本中,该成员将被忽略
iMaxUdpDg:数据报的最大长度。在套接字版本2或以后版本中,该成员将被忽略
lpVendorInfo:套接字厂商信息。在套接字版本2或以后版本中,该成员将被忽略
2.socket
该函数用于创建一个套接字SOCKET socket(int af,int type,int protocol);
af:一个地址家族,通常为AF_INET
type:套接字类型,如果为SOCK_STREAM,表示创建面向链接的流式套接字;为SOCK_DGRAM,表示创建面向无链接的数据报套接字;
为SOCK_RAW,表示创建原始套接字。
potocol:表示套接口所用的协议,如果用户不指定,可以设置为0
返回值:返回创建的套接字句柄
3.bind
该函数用于将套接字绑定到指定的端口和地址上int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);
s:套接字标识
name:一个sockaddr结构指针,该结构包含了要结合的地址和端口号
namelen:确定name缓冲区的长度
返回值:成功为0,否者为SOCKET_ERROR
4.listen
该函数用于将套接字设置为监听模式int listen(SOCKET s,int backlog);
s:套接字标识
backlog:等待连接的最大队列长度。例如,如果backlog=2,此时有三个客户端同时发出连接请求,那么前两个客户端会放置在等待队列中,第三个 客户端会得到错误信息
对于流式套接字,必须处于监听模式才能接受客户端套接字的连接。
5.accept
该函数用于接受客户端的连接,对于流式套接字,必须处于监听状态,才能接受客户端的连接SOCKET accept(SOCTET s,struct sockaddr FAR* addr,int FAR* addrlen);
s:一个套接字,应处于监听状态
addr:一个sockaddr_in结构指针,包含一组客户端的端口号、IP地址等信息
addrlen:用于接受参数addr的长度
返回值:一个新的套接字,它对应于已经接受的客户端连接,对于该客户端的所有后续操作,都应使用这个新的套接字
6.closesocket
该函数用于关闭套接字int closesocket(SOCKET s);
其中,S用于标识一个套接字。如果参数s设置SO_DONTLINGER 选项,则调用该函数后会立即返回,但此时如果有数据尚未传送完毕,会继续传送数据,然后关闭套接字。
7.connect
该函数用于发送一个连接请求int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);
s:一个套接字
name:套接字s想要连接的主机地址和端口号
namelen:name缓冲区的长度
返回值:如果函数执行成功,返回值为0;否则为SOCKET_ERROR,用户可以通过WSAGETLASTERROR得到其错误描述
8.htons
该函数将一个16位的无符号短整型数据由主机排列方式转换为网络排列方式u_shout htons(u_shout hostshort);
返回值:16位的网络排列方式数据
9.htonl
该函数将一个32位的无符号短整型数据由主机排列方式转换为网络排列方式u_shout htons(u_long hostlong);
返回值:32位的网络排列方式数据
10.inet_addr
该函数将一个由字符串表示的地址转换为32位的无符号长整型数据unsigned lon inet_addr(const char FAR* cp);
cp:一个IP地址的字符串
返回值:32位无符号长整型
11.recv
该函数用于从面向连接的套接字中接受数据int recv(SOCKET ,char FAR* buf,int len,int flags);
s:一个套接字
buf:接受数据的缓冲区
len:buf的长度
flags:函数的调用方式,如果为MSG_PEEK,表示查看传来的数据,数据被复制到接受缓冲区,但是不会从输入队列中移走;为MSG_OOB,表示来处理Out-Of-Band数据,也就是带外数据
12.send
该函数用于在面向连接方式的套接字间发送数据int send(SOCKET s,const char FAR buf,int len,int flags);
s:一个套接字
buf:存放要发送数据的缓冲区
len:缓存区长度
flag:函数的调用方式
13.select
该函数用来检查一个或多个套接字是否处于可读可写或错误状态int select(int nfds,fd_set FAR* readfds,fd_set FAR* writefds,fd_set FAR* exceptdfds,const struct timecval FAR* timeout);
nfds:无实际意义
readfds:一组被检查的可读的套接字
writefds:一组被检查可写的套接字
exceptfds:被检查有错误的套接字
timeout:函数的等待时间
14.WSACleanup
该函数用于释放为ws2_32.dll动态链接库初始化时分配的资源int WSACleanup(void);
15.WSAAsyncSelect
该函数用于将网络中发生的事件关联到窗口的某个消息中int WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long IEvent);
s:套接字
hWnd:接受消息的窗口句柄
wMsg:窗口接受来自套接字中的消息
IEvent:网路中发生的事件
16.ioctlsocket
该函数用于设置套接字的I/O模式int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp);
s:待更改I/O模式的套接字
cmd:对套接字的操作命令。cmd=FIOBIO,当argp为0时表示禁止非阻塞模式,当argp为非零时表示设置非阻塞模式。cmd=FIONREAD,表示从套接字中可以读取的数据量;cmd=SIOCATMARK,表示是否所有的带外数据都已被读入,这个命令仅适用于流式套接字,并且该套接字已被设置为可以在线接受带外数据(SO_OOBINLINE)
argp:命令参数
三、网络聊天系统
客户端
1.对话框
2.引用winsock2.h头文件,导入ws2_32.dll库文件
#include "winssock2.h"
#include comment(lib,"ws2_32.dll")
3.在应用程序的IniInstance方法中初始化套接字
WSADATA wsd;//定义WSADATA对象
WSAStartup(MAKEWORD(2,2),&wsd);//初始化套接字
4.改写应用程序的ExitInstance虚方法,在应用程序结束时释放套接字资源
int CClientApp::ExitInstance()
{
WSACleanup(); //释放套接字
return CWinApp::ExitInstance();
}
5.在对话框类中添加成员变量
SOCKET m_SocketClient;//定义一个套接字
UINT m_Port;//定义端口
CString m_IP;//定义IP
6.对话框初始化时创建套接字
BOOL CClientDlg::OnInitDialog()
{
m_SockClient = socket(AF_INET,SOCK_STREAM,0);//创建的是面向链接的流式套接字
}
7.添加ReceiveInfo方法,用于接受服务器端发来的数据
LRESULT CClientDlg::ReceiveInfo(WPARAM wParam, LPARAM lParam)
{
char buffer[1024];//定义一个数据缓冲区
int num = recv(m_SockClient,buffer,1024,0);//接收数据
buffer[num] = 0;//定义结束标记
m_MsgList.AddString(buffer);//读取数据到列表中
//return 0;
}
8.在对话框的消息映射部分添加ON_MESSAGE消息映射宏,将自定义的消息与ReceiveInfo方法关联
ON_MESSAGE(CM_RECEIVE,ReceiveInfo)
9.处理“登录”按钮,开始登录服务器
void CClientDlg::OnLogin()
{
//服务器端地址
sockaddr_in serveraddr;
CString strport;//定义一个字符串,记录端口
m_ServerPort.GetWindowText(strport);//获取端口字符串
m_ServerIP.GetWindowText(m_IP);//获取IP
if (strport.IsEmpty() || m_IP.IsEmpty())//判断端口和IP是否为空
{
MessageBox("请设置服务器IP和端口号");
return;
}
m_Port = atoi(strport);//将端口字符串转换为整数
serveraddr.sin_family = AF_INET;//设置服务器地址家族
serveraddr.sin_port = htons(m_Port);//设置服务器端口号
serveraddr.sin_addr.S_un.S_addr = inet_addr(m_IP);//设置服务器IP
//开始连接服务器
if (connect(m_SockClient,(sockaddr*)&serveraddr,sizeof(serveraddr))!=0)
{
MessageBox("连接失败");
return;
}
else
MessageBox("连接成功");
WSAAsyncSelect(m_SockClient,m_hWnd,1000,FD_READ);//设置异步模型
CString strname,info ;//定义字符串变量
m_NickName.GetWindowText(strname);//获取昵称
info.Format("%s------>%s",strname,"进入聊天室");//设置发送的信息
send(m_SockClient,info.GetBuffer(0),info.GetLength(),0);//向服务器发送数据
}
10.处理“发送”按钮,向服务器发送数据,再由服务器转发数据
void CClientDlg::OnSend()
{
CString strData,name,info ;//定义字符串变量
m_NickName.GetWindowText(name);//获取昵称
m_SendData.GetWindowText(strData);//获取发送的额数据
if (!name.IsEmpty() && !strData.IsEmpty())//判断字符串是否为空
{
info.Format("%s说: %s",name,strData);//格式化发送的数据
//开始发送数据
send(m_SockClient,info.GetBuffer(0),info.GetLength(),0);
m_MsgList.AddString(info);//向列表框添加数据
m_SendData.SetWindowText("");//清空编辑框文本
}
}
11.运行程序
服务器
1.创建对话框
2.引用winsock2.h头文件,导入ws2_32.dll库文件
#include "winssock2.h"
#include comment(lib,"ws2_32.dll")
3.在应用程序的IniInstance方法中初始化套接字
WSADATA wsd;//定义WSADATA对象
WSAStartup(MAKEWORD(2,2),&wsd);//初始化套接字
4.改写应用程序的ExitInstance虚方法,在应用程序结束时释放套接字资源
int CClientApp::ExitInstance()
{
WSACleanup(); //释放套接字
return CWinApp::ExitInstance();
}
5.在对话框类中添加成员变量
SOCKET m_SockServer,m_SockClient;//定义套接字
SOCKET m_Clients[MAXNUM];//客户端套接字
int m_ConnectNum;//当前连接的客户数量
CString m_IP;//定义IP
UINT m_Port;//定义端口
6.对话框初始化时创建套接字
//创建套接字
m_SockServer = socket(AF_INET,SOCK_STREAM,0);
//将网络中的事件关联到窗口的消息函数中
WSAAsyncSelect(m_SockServer,m_hWnd,WM_USER+1,FD_WRITE|FD_READ|FD_ACCEPT);
m_ConnectNum = 0;//初始化客户端链接数量
for (int i = 0; i< MAXNUM;i++)
m_Clients[i]= 0;//初始化客户端套接字
7.对话框添加TranslateData方法,接受客户端的连接,并转发客户端发来的数据
LRESULT CServerDlg::TranslateData(WPARAM wParam, LPARAM lParam)
{
sockaddr_in serveraddr;//定义一个网络地址
char buffer[1024];
int len =sizeof(serveraddr);//获取网络地址大小
//接收客户端的数据
int curlink = -1;//定义整型变量
int num = -1;//定义整形变量
for (int i = 0; i < MAXNUM; i++)//遍历客户端套接字
{
num= recv(m_Clients[i],buffer,1024,0);//获取客户端接受的数据
if (num != -1)//判断哪个客户端向服务器发送数据
{
curlink = i;//记录客户端索引
break;//终止循环
}
}
buffer[num]= 0;//设置数据结束标记
if (num == -1) //接受客户端的连接
{
if (m_ConnectNum < MAXNUM)
{//判断当前客户端连接量是否大于上限
m_Clients[m_ConnectNum] = accept(m_SockServer,(struct sockaddr*)
&serveraddr,&len);
m_ConnectNum++;//将连接数量加1
}
return 1;
}
//将接收的数据发送给客户端
for (int j = 0; j < m_ConnectNum; j++)
if (j != curlink)//不向发送方本身发送数据
send(m_Clients[j],buffer,num,0);
}
8.在对话框的消息映射部分添加ON_MESSAGE消息映射宏,将消息与TranslateData方法关联
ON_MESSAGE(WM_USER+1,TranslateData)
9.处理“设置”按钮,绑定套接字到指定的地址上,使套接字处于监听模式
void CServerDlg::OnSetting()
{
m_ServerIP.GetWindowText(m_IP);
CString strPort;
m_ServerPort.GetWindowText(strPort);
if (m_IP.IsEmpty() || strPort.IsEmpty())
{
MessageBox("请设置服务器IP和端口号","提示");
return;
}
m_Port = atoi(strPort);
sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.S_un.S_addr = inet_addr(m_IP);
serveraddr.sin_port = htons(m_Port);
if (bind(m_SockServer,(sockaddr*)&serveraddr,sizeof(serveraddr)))
{
MessageBox("绑定地址失败.");
return;
}
listen(m_SockServer,20);
}