网络软件设计实验(socket编程实验收获)

实验一:熟悉编程环境

1.winsock需要链接库"wsock32.lib",然后需要使用函数WSAStartup()函数来激活库函数。

2.可以使用函数WSAGetLastError函数来获取上一次调用windows socket函数调用的错误代码,然后通过使用VS2010工具菜单下的错误查找工具,可以找出函数调用错误的原因,可以方便调试使用。

3.在windows socket程序结束时,要使用WSACleanup()来结束程序。 这也是跟linux编程不同的地方。

/-----------------------------------------------------------------------------------------------------------------------------/

实验二:通信程序基本流程

1. 关于INADDR_ANY,参见:点击打开链接

在WINSOCK.h中可以查看到其定义:#define INADDR_ANY              (u_long)0x00000000,这样在调试的时候,代码:

ser_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
它的值为0,也不足为怪了。 这样的设计主要是针对PC是多网卡的情况,表示可以连接多网卡中的任意一个。

2.listen(s,0);  最大连接数为什么设置为0,这时为什么也可以建立连接?

3.关于错误:cannot access the classView informaiton file。是在两个VC窗口中同时打开同一个工程时报的错,解决方案是删除目录下的.ncb文件。

参见:cannot access the classView informaiton file   。这个问题还未解决。

4. 关于调试结束时报错:Please enter the path for CRT0.c,这是VC6.0或其他IDE安装不完整造成的,在别人那里拷个相应的文件到相应的目录:C:\Program Files\Microsoft Visual Studio\VC98\CRT\SRC\CRT0.C ,这样就可以了。

/-----------------------------------------------------------------------------------------------------------------------------/

实验三:关于套接字描述符的实验

1. windows下,socket函数产生的套接字描述符总是从1952开始,以4递减,减到0后,再从2052开始,然后以4为等差递增。

而在linux下,套接字描述符是从3开始的,0、1、2留给了标准输入、标准输出和标准错误。 参见: http://blog.csdn.net/dreamfreelancer/article/details/4335571

2. 套接字描述符是本地分配的,每个进程有各自的套接字描述符空间(每个进程有自己单独的套接字描述符表),标识符只是一个索引,系统会根据进程提供的这个号码,查找进程资源表,找到相应的套接口结构,从而为进城提供通信服务。真正的通信时挂接在套接口结构上的,即使不同的进程具有相同的描述符,也不能使用相同的通信服务。

3.关于条件断点调试的使用。  前面在调试socket描述符分配的使用情况时,里面有很多次循环,想迅速跳到某一次循环,这里可以使用条件断点来解决这个问题。 在edit--breakpoints中,选中断点,点击编辑,可以加入判断终止的条件,这样可以大大提高调试的效率。

/-----------------------------------------------------------------------------------------------------------------------------/

实验四:熟悉套接字常用函数

1. win8上VC6装不好,就转到codeblocks,然后做了一下简单的配置:

同时打开两个窗口,开两个工程:在设置--环境中,把常规设置里的"allow only one running instance“ 和”use an already running instance"的勾给去掉;

2. 关于添加动态库: gcc是直接忽略掉:#pragma comment(lib,"Ws2_32.lib") ,要在链接器中直接添加.  但是现在的状况时服务器端程序可以运行,客户端关于库会报错, 这是个待解决的问题.(这个问题已经解决:服务器端建的C++的文件,而客户端的程序是C的文件,都使用C++的文件就好了,至于为什么还有待研究)。

3. 关于SOCKET类型和SOCKET的值:测试socket函数时,将第3个参数设为10,socket自然设置不成功,调试的时候观察套接字描述符s的返回值,返现s的值为4294967295,当时就很奇怪,socket()如果错误,返回值不应该是-1么?  然后查找SOCKET的定义,为unsigned int,是无符号型数据,这里涉及到无符号数和有符号数之间转换的问题了,运算或者判断系统都会做这些自动转换,无符号数的级别要高些,所以在做运算时,有符号数要自动转换为无符号数。  在做错误判断时,S自然也可以和-1做比较了。   printf中输出无符号数时,要带上“u”这个参数,否则则输出有符号数。

4. connect的等待,在运行客户端时,有时连接不上服务器,需要等待一段时间。关于设置这个等待时间,可以采用select机制来实现(待研究)。

5.accept的返回值--新的套接字描述符:在前面socket创建的套接字描述符是监听套接字(绑定了本地的端口和ip),而accept返回的套接字描述符则代表了一个连接,这个连接包括本地的端口、ip、协议和远端的端口和ip。所以后面的send和recv函数可以直接通过这个套接字描述符直接给对端发送或者从对端接收数据。    在UDP协议中,并没有这个accept函数,只有监听套接字(只包含了本地的信息,并没有远端的信息),所以需要使用sendto和recvfrom(包含远端的信息)函数。

实验五:关于网络字节序

这个问题困扰我很久了,以前也看过这方面的资料,今天晚上通过调试和思考终于搞懂了。先大概记录几个关键点。

1. 为什么会存在网络字节序--这个跟CPU存储数据方式不同有关。MOTO存储数据是高字节的数据优先存储在低字节位置(这样很直观,成为大端存储格式,可以把存储区横着放,地址从低到高,来存储一个整数,比如0x1234,这样刚好对应,一个字节一个字节的填充进去就可以了),而Intel(X86系列)存储数据采用的低字节优先存储在低字节位置(这样比较人的思维习惯,这个称为小端存储)。

2. 为什么要规定网络字节序--因为传输属于系统设计,当然要尽量少地对系统进行改变。这样字节序的改变就发生在用户一侧(也就是PC的应用层上)。

这里就涉及到系统协议的设定。我们知道在缓冲区到时从低地址取数据,然后存放在缓冲区的低地址。这里网络字节序选择了比较直观的存储方式(大端存储),即高字节优先存储的方式。  所以moto的cpu的pc发送数据就不需要做转换,而Intel的需要做转换。

3. 为什么在网络通信中,经常对地址、端口这些数据做转换,而不对发送的数据做转换呢?--其实思考之后也很好理解,网络字节序自然是针对多字节的数据,地址、端口都属于int或者short型数据,而发送或者接收函数中的参数都是字符串常量或者字符数组,这些都是以字节为单位的,自然不需要转换。

4. 怎样检查PC的存储方式?  其实很简单,直接在程序中通过调试,然后查看变量的内存就可以知道了。 不过一般x86系列的都是小端存储,都是需要做转换的。

5. 怎样进行字节序转换呢?   套接字库里给我们提供了接口函数的,比如:htonl/htons/ntol/ntos。函数实现的原理也比较简单,就是一些字节的移位,将一个数字的高字节和低字节完全颠倒即可。不同的系统当然有不同的实现方法。  关于地址这个地方在多说一点,因为地址比较长,4个字节,我们将其一个字节一个字节的拆分,字节之间用.隔开,这样就成了点分十进制的读法,将这个字符串和整数之间也可以做转换,使用转换函数:inet_addr和inet_ntoa两个函数即可。



你可能感兴趣的:(网络软件设计实验(socket编程实验收获))