Windows Socket套接字(一)

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);
}


10.运行程序


  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值