socket学习总结.docx

上传人:b****5 文档编号:8503035 上传时间:2023-01-31 格式:DOCX 页数:52 大小:364.45KB
下载 相关 举报
socket学习总结.docx_第1页
第1页 / 共52页
socket学习总结.docx_第2页
第2页 / 共52页
socket学习总结.docx_第3页
第3页 / 共52页
socket学习总结.docx_第4页
第4页 / 共52页
socket学习总结.docx_第5页
第5页 / 共52页
点击查看更多>>
下载资源
资源描述

socket学习总结.docx

《socket学习总结.docx》由会员分享,可在线阅读,更多相关《socket学习总结.docx(52页珍藏版)》请在冰豆网上搜索。

socket学习总结.docx

socket学习总结

在计算机通信领域,socket被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。

通过socket这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

socket的典型应用就是Web服务器和浏览器:

浏览器获取用户输入的URL,向服务器发起请求,服务器分析接收到的URL,将对应的网页内容返回给浏览器,浏览器再经过解析和渲染,就将文字、图片、视频等元素呈现给用户。

学习socket,也就是学习计算机之间如何通信,并编写出实用的程序。

IP地址(IPAddress)

计算机分布在世界各地,要想和它们通信,必须要知道确切的位置。

确定计算机位置的方式有多种,IP地址是最常用的,例如,114.114.114.114 是国内第一个、全球第三个开放的DNS服务地址,127.0.0.1是本机地址。

其实,我们的计算机并不知道IP地址对应的地理位置,当要通信时,只是将IP地址封装到要发送的数据包中,交给路由器去处理。

路由器有非常智能和高效的算法,很快就会找到目标计算机,并将数据包传递给它,完成一次单向通信。

目前大部分软件使用IPv4地址,但IPv6也正在被人们接受,尤其是在教育网中,已经大量使用。

端口(Port)

有了IP地址,虽然可以找到目标计算机,但仍然不能进行通信。

一台计算机可以同时提供多种网络服务,例如Web服务、FTP服务(文件传输服务)、SMTP服务(邮箱服务)等,仅有IP地址,计算机虽然可以正确接收到数据包,但是却不知道要将数据包交给哪个网络程序来处理,所以通信失败。

为了区分不同的网络程序,计算机会为每个网络程序分配一个独一无二的端口号(PortNumber),例如,Web服务的端口号是80,FTP服务的端口号是21,SMTP服务的端口号是25。

端口(Port)是一个虚拟的、逻辑上的概念。

可以将端口理解为一道门,数据通过这道门流入流出,每道门有不同的编号,就是端口号。

如下图所示:

协议(Protocol)

协议(Protocol)就是网络通信的约定,通信的双方必须都遵守才能正常收发数据。

协议有很多种,例如TCP、UDP、IP等,通信的双方必须使用同一协议才能通信。

协议是一种规范,由计算机组织制定,规定了很多细节,例如,如何建立连接,如何相互识别等。

协议仅仅是一种规范,必须由计算机软件来实现。

例如IP协议规定了如何找到目标计算机,那么各个开发商在开发自己的软件时就必须遵守该协议,不能另起炉灶。

所谓协议族(ProtocolFamily),就是一组协议(多个协议)的统称。

最常用的是TCP/IP协议族,它包含了TCP、IP、UDP、Telnet、FTP、SMTP等上百个互为关联的协议,由于TCP、IP是两种常用的底层协议,所以把它们统称为TCP/IP协议族。

数据传输方式

计算机之间有很多数据传输方式,各有优缺点,常用的有两种:

SOCK_STREAM和SOCK_DGRAM。

1)SOCK_STREAM表示面向连接的数据传输方式。

数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。

常见的http协议就使用SOCK_STREAM传输数据,因为要确保数据的正确性,否则网页不能正常解析。

2)SOCK_DGRAM表示无连接的数据传输方式。

计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。

也就是说,数据错了就错了,无法重传。

因为SOCK_DGRAM所做的校验工作少,所以效率比SOCK_STREAM高。

QQ视频聊天和语音聊天就使用SOCK_DGRAM传输数据,因为首先要保证通信的效率,尽量减小延迟,而数据的正确性是次要的,即使丢失很小的一部分数据,视频和音频也可以正常解析,最多出现噪点或杂音,不会对通信质量有实质的影响。

注意:

SOCK_DGRAM没有想象中的糟糕,不会频繁的丢失数据,数据错误只是小概率事件。

有可能多种协议使用同一种数据传输方式,所以在socket编程中,需要同时指明数据传输方式和协议。

综上所述:

IP地址和端口能够在广袤的互联网中定位到要通信的程序,协议和数据传输方式规定了如何传输数据,有了这些,两台计算机就可以通信了。

和C语言教程一样,我们从一个简单的“HelloWorld!

”程序切入socket编程。

本节演示了Linux下的代码,server.cpp是服务器端代码,client.cpp是客户端代码,要实现的功能是:

客户端从服务器读取一个字符串并打印出来。

服务器端代码server.cpp:

1.#include

2.#include

3.#include

4.#include

5.#include

6.#include

7.#include

8.

9.intmain(){

10.//创建套接字

11.intserv_sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

12.

13.//将套接字和IP、端口绑定

14.structsockaddr_inserv_addr;

15.memset(&serv_addr,0,sizeof(serv_addr)); //每个字节都用0填充

16.serv_addr.sin_family=AF_INET;//使用IPv4地址

17.serv_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//具体的IP地址

18.serv_addr.sin_port=htons(1234);//端口

19.bind(serv_sock,(structsockaddr*)&serv_addr,sizeof(serv_addr));

20.

21.//进入监听状态,等待用户发起请求

22.listen(serv_sock,20);

23.

24.//接收客户端请求

25.structsockaddr_inclnt_addr;

26.socklen_tclnt_addr_size=sizeof(clnt_addr);

27.intclnt_sock=accept(serv_sock,(structsockaddr*)&clnt_addr,&clnt_addr_size);

28.

29.//向客户端发送数据

30.charstr[]="HelloWorld!

";

31.write(clnt_sock,str,sizeof(str));

32.

33.//关闭套接字

34.close(clnt_sock);

35.close(serv_sock);

36.

37.return0;

38.}

客户端代码client.cpp:

1.#include

2.#include

3.#include

4.#include

5.#include

6.#include

7.

8.intmain(){

9.//创建套接字

10.intsock=socket(AF_INET,SOCK_STREAM,0);

11.

12.//向服务器(特定的IP和端口)发起请求

13.structsockaddr_inserv_addr;

14.memset(&serv_addr,0,sizeof(serv_addr));//每个字节都用0填充

15.serv_addr.sin_family=AF_INET;//使用IPv4地址

16.serv_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//具体的IP地址

17.serv_addr.sin_port=htons(1234);//端口

18.connect(sock,(structsockaddr*)&serv_addr,sizeof(serv_addr));

19.

20.//读取服务器传回的数据

21.charbuffer[40];

22.read(sock,buffer,sizeof(buffer)-1);

23.

24.printf("Messageformserver:

%s\n",buffer);

25.

26.//关闭套接字

27.close(sock);

28.

29.return0;

30.}

先编译server.cpp并运行:

[admin@localhost~]$g++server.cpp-oserver

[admin@localhost~]$./server

|

正常情况下,程序运行到 accept()函数就会被阻塞,等待客户端发起请求。

接下来编译client.cpp并运行:

[admin@localhost~]$g++client.cpp-oclient

[admin@localhost~]$./client

Messageformserver:

 HelloWorld!

[admin@localhost~]$

client运行后,通过connect()函数向server发起请求,处于监听状态的server被激活,执行accept()函数,接受客户端的请求,然后执行write()函数向client传回数据。

client接收到传回的数据后,connect()就运行结束了,然后使用read()将数据读取出来。

需要注意的是:

1)server只接受一次client请求,当server向client传回数据后,程序就运行结束了。

如果想再次接收到服务器的数据,必须再次运行server,所以这是一个非常简陋的socket程序,不能够一直接受客户端的请求。

2)上面的源文件后缀为.cpp,是C++代码,所以要用g++命令来编译。

C++和C语言的一个重要区别是:

在C语言中,变量必须在函数的开头定义;而在C++中,变量可以在函数的任何地方定义,使用更加灵活。

这里之所以使用C++代码,是不希望在函数开头堆砌过多变量。

源码解析

1)先说一下server.cpp中的代码。

第11行通过socket()函数创建了一个套接字,参数 AF_INET表示使用IPv4地址,SOCK_STREAM表示使用面向连接的数据传输方式,IPPROTO_TCP表示使用TCP协议。

在Linux中,socket也是一种文件,有文件描述符,可以使用write()/read()函数进行I/O操作。

第19行通过bind()函数将套接字serv_sock与特定的IP地址和端口绑定,IP地址和端口都保存在sockaddr_in结构体中。

socket()函数确定了套接字的各种属性,bind()函数让套接字与特定的IP地址和端口对应起来,这样客户端才能连接到该套接字。

第22行让套接字处于被动监听状态。

所谓被动监听,是指套接字一直处于“睡眠”中,直到客户端发起请求才会被“唤醒”。

第27行的accept()函数用来接收客户端的请求。

程序一旦执行到accept()就会被阻塞(暂停运行),直到客户端发起请求。

第31行的write()函数用来向套接字文件中写入数据,也就是向客户端发送数据。

和普通文件一样,socket在使用完毕后也要用close()关闭。

2)再说一下client.cpp中的代码。

client.cpp中的代码和server.cpp中有一些区别。

第19行代码通过connect()向服务器发起请求,服务器的IP地址和端口号保存在sockaddr_in结构体中。

直到服务器传回数据后,connect()才运行结束。

第23行代码通过read()从套接字文件中读取数据。

上节演示了Linux下的socket程序,这节来看一下Windows下的socket程序。

同样,server.cpp为服务器端代码,client为客户端代码。

服务器端代码server.cpp:

1.#include

2.#include

3.#pragmacomment(lib,"ws2_32.lib")//加载ws2_32.dll

4.

5.intmain(){

6.//初始化DLL

7.WSADATAwsaData;

8.WSAStartup(MAKEWORD(2,2),&wsaData);

9.

10.//创建套接字

11.SOCKETservSock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

12.

13.//绑定套接字

14.sockaddr_insockAddr;

15.memset(&sockAddr,0,sizeof(sockAddr));//每个字节都用0填充

16.sockAddr.sin_family=PF_INET;//使用IPv4地址

17.sockAddr.sin_addr.s_addr=inet_addr("127.0.0.1");//具体的IP地址

18.sockAddr.sin_port=htons(1234);//端口

19.bind(servSock,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));

20.

21.//进入监听状态

22.listen(servSock,20);

23.

24.//接收客户端请求

25.SOCKADDRclntAddr;

26.intnSize=sizeof(SOCKADDR);

27.SOCKETclntSock=accept(servSock,(SOCKADDR*)&clntAddr,&nSize);

28.

29.//向客户端发送数据

30.char*str="HelloWorld!

";

31.send(clntSock,str,strlen(str)+sizeof(char),NULL);

32.

33.//关闭套接字

34.closesocket(clntSock);

35.closesocket(servSock);

36.

37.//终止DLL的使用

38.WSACleanup();

39.

40.return0;

41.}

客户端代码client.cpp:

1.#include

2.#include

3.#include

4.#pragmacomment(lib,"ws2_32.lib")//加载ws2_32.dll

5.

6.intmain(){

7.//初始化DLL

8.WSADATAwsaData;

9.WSAStartup(MAKEWORD(2,2),&wsaData);

10.

11.//创建套接字

12.SOCKETsock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

13.

14.//向服务器发起请求

15.sockaddr_insockAddr;

16.memset(&sockAddr,0,sizeof(sockAddr));//每个字节都用0填充

17.sockAddr.sin_family=PF_INET;

18.sockAddr.sin_addr.s_addr=inet_addr("127.0.0.1");

19.sockAddr.sin_port=htons(1234);

20.connect(sock,(SOCKADDR*)&sockAddr,sizeof(SOCKADDR));

21.

22.//接收服务器传回的数据

23.charszBuffer[MAXBYTE]={0};

24.recv(sock,szBuffer,MAXBYTE,NULL);

25.

26.//输出接收到的数据

27.printf("Messageformserver:

%s\n",szBuffer);

28.

29.//关闭套接字

30.closesocket(sock);

31.

32.//终止使用DLL

33.WSACleanup();

34.

35.system("pause");

36.return0;

37.}

将server.cpp和client.cpp分别编译为server.exe和client.exe,先运行server.exe,再运行client.exe,输出结果为:

Messageformserver:

HelloWorld!

Windows下的socket程序和Linux思路相同,但细节有所差别:

1)Windows下的socket程序依赖 Winsock.dll或 ws2_32.dll,必须提前加载。

DLL有两种加载方式,请查看:

动态链接库DLL的加载

2)Linux使用“文件描述符”的概念,而Windows使用“文件句柄”的概念;Linux不区分socket文件和普通文件,而Windows区分;Linux下socket()函数的返回值为int类型,而Windows下为SOCKET类型,也就是句柄。

3)Linux下使用read()/write()函数读写,而Windows下使用recv()/send()函数发送和接收。

4)关闭socket时,Linux使用close()函数,而Windows使用closesocket()函数。

本节讲解Windows下DLL的加载,学习LinuxSocket的读者可以跳过。

WinSock(WindowsSocket)编程依赖于系统提供的动态链接库(DLL),有两个版本:

∙较早的DLL是 wsock32.dll,大小为28KB,对应的头文件为winsock1.h;

∙最新的DLL是 ws2_32.dll,大小为69KB,对应的头文件为 winsock2.h。

几乎所有的Windows操作系统都已经支持ws2_32.dll,包括个人操作系统Windows95OSR2、Windows98、WindowsMe、Windows2000、XP、Vista、Win7、Win8、Win10以及服务器操作系统WindowsNT4.0SP4、WindowsServer2003、WindowsServer2008等,所以你可以毫不犹豫地使用最新的ws2_32.dll。

使用DLL之前必须把DLL加载到当前程序,你可以在编译时加载,也可以在程序运行时加载,《C语言高级教程》中讲到了这两种加载方式,请猛击:

动态链接库DLL的加载:

隐式加载(载入时加载)和显式加载(运行时加载)。

这里使用#pragma命令,在编译时加载:

#pragmacomment(lib,"ws2_32.lib")

WSAStartup()函数

使用DLL之前,还需要调用 WSAStartup()函数进行初始化,以指明WinSock规范的版本,它的原型为:

intWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);

wVersionRequested为WinSock规范的版本号,低字节为主版本号,高字节为副版本号(修正版本号);lpWSAData为指向WSAData结构体的指针。

关于WinSock规范

WinSock规范的最新版本号为2.2,较早的有2.1、2.0、1.1、1.0,ws2_32.dll支持所有的规范,而wsock32.dll仅支持1.0和1.1。

wsock32.dll已经能够很好的支持TCP/IP通信程序的开发,ws2_32.dll主要增加了对其他协议的支持,不过建议使用最新的 2.2 版本。

wVersionRequested参数用来指明我们希望使用的版本号,它的类型为WORD,等价于 unsignedshort,是一个整数,所以需要用MAKEWORD()宏函数对版本号进行转换。

例如:

MAKEWORD(1,2);//主版本号为1,副版本号为2,返回0x0201

MAKEWORD(2,2);//主版本号为2,副版本号为2,返回0x0202

关于WSAData结构体

WSAStartup()函数执行成功后,会将与ws2_32.dll有关的信息写入WSAData结构体变量。

WSAData的定义如下:

1.typedefstructWSAData{

2.WORDwVersion;//ws2_32.dll建议我们使用的版本号

3.WORDwHighVersion;//ws2_32.dll支持的最高版本号

4.//一个以null结尾的字符串,用来说明ws2_32.dll的实现以及厂商信息

5.charszDescription[WSADESCRIPTION_LEN+1];

6.//一个以null结尾的字符串,用来说明ws2_32.dll的状态以及配置信息

7.charszSystemStatus[WSASYS_STATUS_LEN+1];

8.unsignedshortiMaxSockets;//2.0以后不再使用

9.unsignedshortiMaxUdpDg;//2.0以后不再使用

10.charFAR*lpVendorInfo;//2.0以后不再使用

11.}WSADATA,*LPWSADATA;

最后3个成员已弃之不用,szDescription和 szSystemStatus包含的信息基本没

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 日语学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1