博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Socket编程详解
阅读量:6388 次
发布时间:2019-06-23

本文共 7585 字,大约阅读时间需要 25 分钟。

1  计算机网络基础

1.1 OSI参考模型(略)

1.2 TCP/IP协议

 传输控制协议/网际协议,TCP/IP将网络分为四层,从高往低一次为应用层-》传输层-》网络层-》数据链路层。其中,传输层协议包括TCP,UDP;网络层协议包括IP、ARP等;

如图所示:

注:图片来源http://blog.csdn.net/lizhifeng2009/article/details/8820228

2  套接字编程基础

2.1  套接字

套接字是网络编程的基础,最初由加利福尼亚大学为UNIX开发的网络通信编程接口,为了在WINDOWS 操作系统上使用,微软与第三方厂商共同制定了一套标准,Winsock.。

套接字实际上是一个指向传输提供者的句柄,Winsock中通过该句柄实现网络通信与管理。

可以理解为实现网络通信和管理,在应用层与传输层之间的操作接口。对于用户而言,可通过调用SOCKET 相关接口函数,控制传输协议、传输内容等。

具体如图所示:

 

2.1.1 套接字类型

 根据套接字作用不同,分为三类:原始套接字、流式套接字和数据包套接字。

原始套接字(SOCK_RAW):能够使程序人员对底层的网络机制进行控制,接收的数据包头中含有IP头。

流式套接字(SOCK_STREAM):提供了双向、有序、可靠的数据传输服务,在通信前需要双方建立连接,TCP协议采用了该套接字。

数据包套接字(SOCK_DGRAM):与流式套接字相对应,提供双向数据流,但是不能保证数据传输的可靠性、有序性和无重复性。UDP协议采用了该套接字。

//2.2 套接字I/O模型

(暂缺)

2.3 套接字函数

为了使用套接字进行网络程序开发,Windows操作系统提供了一组套接字函数,使用这些函数,可以实现功能强大的网络编程。常用可分为三大类:初始化库函数(WSAStartup()、WSACleanup())、通信时使用函数(socket()、bind()、listen()、accept()、closesocket()、connect()、recv()、send()、select()、ioctlsocket())和地址转换函数(htons()、htonl()、inet_addr())。其中,TCP的三次握手是通过connect 函数触发。

其中,WSAStartup()用于初始化动态链接库函数,WSACleanup()用于释放动态链接库初始化时分配的资源。

注:套接字函数通常封装在Ws2_32.dll动态链接库中,其头文件Winsock2.h提供了套接字函数的原型,库文件Ws2_32.lib提供了Ws2_32.dll文件的输出节,在使用套接字函数前,需包含头文件 ,并链接Ws2_32.lib。

#include 
#pragma comment(lib,"ws2_32.lib")

 

3  网络编程步骤

实现网络间通信,需要一个服务器端和一个客户端。这两者之间的具体实现有点不同,大体步骤为:

3.1 服务器端

(1)创建套接字:socket()创建套接字,指明传输协议  

例如:
sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//sListen代表返回的套接字 //AF_INET为IPV4地址家族
//SOCK_STREAM为套接字类型 //IPPROTO_TCP指明传输协议

(2)构建本地地址信息  

//构建本地地址信息      saServer.sin_family = AF_INET; //地址家族      saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序      saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址

(3)bind()函数把一个地址族中的特定地址赋给socket。

//绑定ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));      if (ret == SOCKET_ERROR)      {          printf("bind() faild! code:%d\n", WSAGetLastError());          closesocket(sListen); //关闭套接字          WSACleanup();          //return 0;      }

(4)监听:listen()

//侦听连接请求      ret = listen(sListen, 5);      if (ret == SOCKET_ERROR)      {          printf("listen() faild! code:%d\n", WSAGetLastError());          closesocket(sListen); //关闭套接字          //return 0;      }

(5)等待连接:accept()

accept()函数提取出所监听套接字的等待连接队列中第一个连接请求创建一个新的套接字,并返回指向该套接字的文件描述符;

函数原型为:int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

3.2 客户端

(1)创建套接字

(2)构建服务器地址

(3)连接服务器connect()

4  实例

下面通过一个简单的客户端/服务器端程序,讲解通信编程的具体实现:

//服务器端// Server.cpp : 定义控制台应用程序的入口点。//TCP测试程序的服务器端程序#include "stdafx.h"  #include 
#include
#pragma comment(lib,"ws2_32.lib") #define SERVER_PORT 5208 //侦听端口 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; int ret, nLeft, length; SOCKET sListen, sServer; //侦听套接字,连接套接字 struct sockaddr_in saServer, saClient; //地址信息 char *ptr;//用于遍历信息的指针 //WinSock初始化 wVersionRequested=MAKEWORD(2, 2); //希望使用的WinSock DLL 的版本 ret=WSAStartup(wVersionRequested, &wsaData); if(ret!=0) { printf("WSAStartup() failed!\n"); //return 0; } //创建Socket,使用TCP协议 sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sListen == INVALID_SOCKET) { WSACleanup(); printf("socket() faild!\n"); //return 0; } //构建本地地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序 saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址 //绑定 ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("bind() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 WSACleanup(); //return 0; } //侦听连接请求 ret = listen(sListen, 5); if (ret == SOCKET_ERROR) { printf("listen() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 //return 0; } printf("Waiting for client connecting!\n"); printf("Tips: Ctrl+c to quit!\n"); //阻塞等待接受客户端连接 while(1)//循环监听客户端,永远不停止 { length = sizeof(saClient); sServer = accept(sListen, (struct sockaddr *)&saClient, &length); if (sServer == INVALID_SOCKET) { printf("accept() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 WSACleanup(); return 0; } char sendMessage[]="\nhello client"; //发送信息给客户端 send(sServer,sendMessage,strlen(sendMessage)+1,0); char receiveMessage[5000]; nLeft = sizeof(receiveMessage); ptr = (char *)&receiveMessage; while(nLeft>0) { //接收数据 ret = recv(sServer, ptr, 5000, 0); //非负,成功;-1,失败 if (ret == SOCKET_ERROR) { printf("recv() failed!\n"); return 0; } if (ret == 0) //客户端已经关闭连接 { printf("Client has closed the connection\n"); break; } nLeft -= ret; ptr += ret; } printf("receive message:%s\n", receiveMessage);//打印我们接收到的消息。 } // closesocket(sListen); // closesocket(sServer); // WSACleanup(); return 0; }
//TCP测试程序的客户端程序#include "stdafx.h"  #include 
#include
#include
#pragma comment(lib,"ws2_32.lib")#define SERVER_PORT 5208 //侦听端口 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; //WSADATA为Windows套接字结构体 int ret; SOCKET sClient; //连接套接字 struct sockaddr_in saServer; //服务器地址信息 char *ptr; BOOL fSuccess = TRUE; //--------------------------------------------------------------- //WinSock初始化 //--------------------------------------------------------------- wVersionRequested = MAKEWORD(2, 2); //希望使用的WinSock DLL的版本 ret = WSAStartup(wVersionRequested, &wsaData); //加载套接字库 if(ret!=0) { printf("WSAStartup() failed!\n"); //return 0; } //确认WinSock DLL支持版本2.2 if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2) { WSACleanup(); //释放为该程序分配的资源,终止对winsock动态库的使用 printf("Invalid WinSock version!\n"); //return 0; } //创建Socket,使用TCP协议 sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sClient == INVALID_SOCKET) { WSACleanup(); printf("socket() failed!\n"); //return 0; } //构建服务器地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意转化为网络节序 saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //连接服务器 ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("connect() failed!\n"); closesocket(sClient); //关闭套接字 WSACleanup(); //return 0; } char sendMessage[]="hahaha!!!"; ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0); if (ret == SOCKET_ERROR) { printf("send() failed!\n"); } else printf("client info has been sent!"); char recvBuf[100]; recv(sClient,recvBuf,100,0); printf("%s\n",recvBuf); closesocket(sClient); //关闭套接字 WSACleanup(); //getchar(); 没啥用,让你最后在显示终端可以输入一串字符,但是不能发送 //return 0; }

 

转载于:https://www.cnblogs.com/Miss-Bueno/p/7435319.html

你可能感兴趣的文章
git merge 和 git rebase 小结
查看>>
OpenStack搭建高可用RabbitMQ集群
查看>>
IOS开发-UIView之动画效果的实现方法(合集)
查看>>
[8-20]Linux常用目录pwd,ls,cd目录和stat,echo基本用法
查看>>
QML入门教程(2)
查看>>
shell基础的几张图片
查看>>
記錄一次CRS-0184: Cannot communicate with the CRS daemon的解決
查看>>
android的消息处理机制(图+源码分析)——Looper,Handler,Message
查看>>
转:阴影锥原理与展望—真实的游戏效果的实现
查看>>
欧拉角转四元数 笔记
查看>>
Unity3D使用TCP/IP协议,传递protocol buffer消息protobuf-net
查看>>
大端模式与小端模式、网络字节顺序与主机字节顺序
查看>>
MathType怎么删除常用公式
查看>>
REST API (from IBM)
查看>>
ParagraphString - 段落样式的简易处理
查看>>
前端使用AngularJS的$resource,后端ASP.NET Web API,实现增删改查
查看>>
面向对象设计原则
查看>>
第四十五课 分布式系统、大型网络架构、MogileFS 基础应用
查看>>
yum问题的解决办法
查看>>
转载如何具体优化网站关键词的?(三)
查看>>