socket学习总结Word格式.docx
- 文档编号:21639265
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:52
- 大小:364.45KB
socket学习总结Word格式.docx
《socket学习总结Word格式.docx》由会员分享,可在线阅读,更多相关《socket学习总结Word格式.docx(52页珍藏版)》请在冰豆网上搜索。
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<
stdio.h>
2.#include<
string.h>
3.#include<
stdlib.h>
4.#include<
unistd.h>
5.#include<
arpa/inet.h>
6.#include<
sys/socket.h>
7.#include<
netinet/in.h>
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:
7.
8.intmain(){
9.//创建套接字
10.intsock=socket(AF_INET,SOCK_STREAM,0);
11.
12.//向服务器(特定的IP和端口)发起请求
13.structsockaddr_inserv_addr;
14.memset(&
15.serv_addr.sin_family=AF_INET;
16.serv_addr.sin_addr.s_addr=inet_addr("
17.serv_addr.sin_port=htons(1234);
18.connect(sock,(structsockaddr*)&
19.
20.//读取服务器传回的数据
21.charbuffer[40];
22.read(sock,buffer,sizeof(buffer)-1);
23.
24.printf("
Messageformserver:
%s\n"
buffer);
25.
26.//关闭套接字
27.close(sock);
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
[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为客户端代码。
winsock2.h>
3.#pragmacomment(lib,"
ws2_32.lib"
)//加载ws2_32.dll
4.
5.intmain(){
6.//初始化DLL
7.WSADATAwsaData;
8.WSAStartup(MAKEWORD(2,2),&
wsaData);
9.
11.SOCKETservSock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
13.//绑定套接字
14.sockaddr_insockAddr;
sockAddr,0,sizeof(sockAddr));
16.sockAddr.sin_family=PF_INET;
17.sockAddr.sin_addr.s_addr=inet_addr("
18.sockAddr.sin_port=htons(1234);
19.bind(servSock,(SOCKADDR*)&
sockAddr,sizeof(SOCKADDR));
21.//进入监听状态
22.listen(servSock,20);
25.SOCKADDRclntAddr;
26.intnSize=sizeof(SOCKADDR);
27.SOCKETclntSock=accept(servSock,(SOCKADDR*)&
clntAddr,&
nSize);
30.char*str="
31.send(clntSock,str,strlen(str)+sizeof(char),NULL);
32.
34.closesocket(clntSock);
35.closesocket(servSock);
37.//终止DLL的使用
38.WSACleanup();
39.
40.return0;
41.}
WinSock2.h>
4.#pragmacomment(lib,"
5.
6.intmain(){
7.//初始化DLL
8.WSADATAwsaData;
9.WSAStartup(MAKEWORD(2,2),&
10.
11.//创建套接字
12.SOCKETsock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
13.
14.//向服务器发起请求
15.sockaddr_insockAddr;
16.memset(&
17.sockAddr.sin_family=PF_INET;
18.sockAddr.sin_addr.s_addr=inet_addr("
19.sockAddr.sin_port=htons(1234);
20.connect(sock,(SOCKADDR*)&
21.
22.//接收服务器传回的数据
23.charszBuffer[MAXBYTE]={0};
24.recv(sock,szBuffer,MAXBYTE,NULL);
25.
26.//输出接收到的数据
27.printf("
szBuffer);
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,输出结果为:
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,"
)
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;
10.charFAR*lpVendorInfo;
11.}WSADATA,*LPWSADATA;
最后3个成员已弃之不用,szDescription和
szSystemStatus包含的信息基本没
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- socket 学习 总结