参考资料:Winsocket编程之套接字原理
Socket来自U.C.Berkele的UNIX操作系统上的网络接口,后来逐步移植到Windows,Windows Socket规范起源于90年代初,由SUN,JSB,FTP,MICRODYNE和MICROSOFT共同制定。依次有1.0,1.1,2.0和3.0。
Windows Soket规范定义并记录了如何使用API与Internet/Intranet协议连接,Socket是网络通信的基本操作单元,本地主机和远地主机通过对Socket的读写来完成网络之间的相互通信。一个正在被使用的Socket都有它的类型和与其相关的进程。
目前Windows支持两种套接口:流套接口和数据报套接口。TCP/IP中的TCP协议使用的就是流套接口,提供了面向连接的,无差错的,发送先后顺序一致,包长度不限和非重复的网络信包传输。TCP/IP中的UDP则使用数据报套接口,该接口提供双向的,有序的,无重复并无记录边界的数据流服务,以独立的数据包进行网络传输,信包最大长度32KB,传输不保证顺序性,可靠性和无重复性。数据报支持双向的数据流。
Windows Socket使用客户机/服务器模式,在这里分别对两种进行介绍:
服务器要先启动:
1、打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。
2、等待客户请求到达该端口。
3、接收到重复服务请求,处理该请求并发送应答信号。
4、返回第二步,等待另一客户请求。
5、关闭服务器。
客户方:
1、打开一通信通道,并连接到服务器所在主机的特定端口。
2、向服务器发送服务请求报文,等待并接收应答;继续提出请求……
3、请求结束后关闭通信通道并终止。
基本套接字接口:
1、创建套接字——socket()
2、指定本地地址——bind()
3、建立套接字连接——connect()和accept()
4、监听连接——listen()
5、数据传输——send()与recv()
6、多路复用——select()
7、关闭套接字——closesocket()
面向连接的套接字的系统调用时序图
无连接协议的套接字调用时序图
面向连接的应用程序流程图
MFC实际程序范例:
客户端:
加入wsock32.lib。
- #include "winsock.h"
- #define PORT 7777
- char TARGET_ADDRESS[] = "10.167.16.56";
-
- void CDlg::OnSend()
- {
- char Buffer[1000];
- WSADATA wsaData;
- int retval;
- struct sockaddr_in server;
- SOCKET conn_socket;
-
- this->UpdateData(true);
- strcpy(Buffer,m_send);
-
-
- if(WSAStartup(0x202,&wsaData)!=0)
- {
- AfxMessageBox("Socket 初始化错误");
- WSACleanup();
- return;
- }
-
- server.sin_family = AF_INET;
- server.sin_addr.s_addr = inet_addr(TARGET_ADDRESS);
- server.sin_port = htons(PORT);
-
-
- conn_socket = socket(AF_INET,SOCK_STREAM,0);
- if(conn_socket < 0)
- {
- AfxMessageBox("Socket 错误");
- return;
- }
-
-
- if(connect(conn_socket,(LPSOCKADDR)&server,sizeof(SOCKADDR))
- == SOCKET_ERROR)
- {
- int i = WSAGetLastError();
- AfxMessageBox("连接失败");
- return;
- }
-
-
- retval = send(conn_socket,Buffer,m_send.GetLength(),0);
- closesocket(conn_socket);
- return;
- }
服务器端:
加入wsock32.lib。
- #include "winsock2.h"
- #define PORT 7777
-
- void CDlg::OnRecv()
- {
- WSADATA wsaData;
- sockaddr_in local;
- SOCKET listen_socket,msg_socket;
- int retval;
- char Buffer[1000];
- sockaddr_in from;
- int fromlen;
-
-
- if(WSAStartup(0x202,&wsaData) != 0)
- {
- AfxMessageBox("Socket初始化错误");
- WSACleanup();
- return;
- }
-
- local.sin_family = AF_INET;
- local.sin_addr.s_addr = INADDR_ANY;
- local.sin_port = htons(PORT);
-
-
- listen_socket = socket(AF_INET,SOCK_STREAM,0);
- if(listen_socket == INVALID_SOCKET)
- {
- AfxMessageBox("socket产生错误");
- WSACleanup();
- return;
- }
-
-
- if(bind(listen_socket,(sockaddr *)&local,sizeof(local)) != 0)
- {
- AfxMessageBox("socket绑定错误");
- WSACleanup();
- return;
- }
-
- fromlen = sizeof(SOCKADDR);
-
-
- if(listen(listen_socket,5) != 0)
- {
- AfxMessageBox("监听出错");
- WSACleanup();
- return;
- }
-
-
-
- do{
- if((msg_socket = accept(listen_socket,(LPSOCKADDR)&from,&fromlen))
- == INVALID_SOCKET)
- {
- int i = WSAGetLastError();
- WSACleanup();
- AfxMessageBox("接收错误");
- return;
- }
- memset(Buffer,0,sizeof(Buffer));
-
- do{
- if((retval = recv(msg_socket,Buffer,sizeof(Buffer),0)) < 0)
- {
- AfxMessageBox("接收失败");
- return;
- }
- }
- while(retval != 0);
- AfxMessageBox(Buffer);
- closesocket(msg_socket);
- }while(1);
-
- closesocket(listen_socket);
- }
以上代码在对话框中实现了一个最基本的消息发送功能,不足的地方是监听是做在主线程里面的,而且不能发送到内网。
(fanged) |