当前位置:首页 > Linux Socket 编程实验
实验 6. Linux Socket 编程实验
1、实验目的:
(1) 了解 TCP/IP 协议; (2) 掌握 socket 编程。 2、实验设备:
(1) PC 机的 VMware 虚拟机运行 Ubuntu Linux 系统; (2) 两机对连的网络线; (3) 带网口的测试计算机;
(4) WINDOWS“SOCKET TOOL”调试工具。 3、实验内容:
实现典型客户机/服务器程序中的服务器及客户机。 4、实验原理
4.1 客户机/服务器工作流程
使用TCP协议的客户机/服务器进程的工作过程如下图 4.2 Socket 编程相关函数
常用的socket函数有:socket,bind,listen,accept,connect,send,recv。 1)socket(建立连接) 表头文件:
#include
定义函数:int socket(int family,int type,int protocol);
函数说明:socket()函数用来生成一个套接口描述字,也称为套接字,指 定协议簇和套接口。
参数:family指定协议族,type指明字节流方式,而protocol一般为0 Family的取值范围: AF_LOCALUNIX协议族 AF_ROUTE路由套接口 AF_INETIPv4协议 AF_INET6IPv6协议 AF_KEY密钥套接口 参数type的取值范围: SOCK_STREAMTCP套接口 SOCK_DGRAMUDP套接口
SOCK_PACKET支持数据链路访问 SOCK_RAM原始套接口
返回值:成功返回非负描述字,失败返回负值 2)bind(对socket定位) 表头文件:
#include
Int bind(int sockfd,struct sockaddr * my_addr,int addrlen);
函数说明bind()用来设置给参数sockfd的socket一个名称。此名称由参数 my_addr指向一
sockaddr结构,对于不同的socketdomain定义了一个通用的数据结构 structsockaddr {
unsignedshortintsa_family; charsa_data[14]; };
sa_family为调用socket()时的domain参数,即AF_xxxx值。 sa_data最多使用14个字符长度。
此sockaddr结构会因使用不同的socketdomain而有不同结构定义, 例如使用 AF_INET
domain,其socketaddr结构定义便为 structsocketaddr_in {
unsignedshortintsin_family; uint16_tsin_port;
structin_addrsin_addr; unsignedcharsin_zero[8]; };
structin_addr {
uint32_ts_addr; };
sin_family即为sa_family sin_port为使用的port编号 sin_addr.s_addr为IP地址 sin_zero未使用。
参数socket为套接字,my_addr是一个指向特定协议地址结构的指针, addrlen为sockaddr 的结构长度。
返回值成功则返回0,失败返回-1,错误原因存于errno中。 错误代码
EBADF参数sockfd非合法socket处理代码。 EACCESS权限不足
ENOTSOCK参数sockfd为一文件描述词,非socket。 3)listen(等待连接) 表头文件:
#include
Int listen(ints,intbacklog); 函数说明:
listen()用来等待参数s的socket连线。 参数backlog指定同时能处理的最大
连接要求,如果连接数目达此上限则client端将收到ECONNREFUSED的错误。 Listen()并未开始接收连线,只是设置socket为listen模式,真正接收client 端连线的是accept()。通常listen()会在socket(),bind()之后调用,接着才 调用accept()。
返回值成功则返回0,失败返回-1,错误原因存于errno
附加说明listen()只适用SOCK_STREAM或SOCK_SEQPACKET的socket类型。如 果
socket为AF_INET则参数backlog最大值可设至128。 错误代码
EBADF参数sockfd非合法socket处理代码 EACCESS权限不足
EOPNOTSUPP指定的socket并未支援listen模式。 4)accept 表头文件:
#include
Int accept(int s,struct sockaddr * addr,int * addrlen);
函数说明accept()用来接受参数s的socket连线。参数s的socket必需先经 bind()、
listen()函数处理过,
当有连线进来时accept()会返回一个新的socket
处理代码,往后的数据传送与读取就是经由新的socket处理,而原来参数s的 socket能继续使用accept()来接受新的连线要求。连线成功时,参数addr所指 的结构会被系统填入远程主机的地址数据,参数addrlen为scokaddr的结构长 度。关于结构sockaddr的定义请参考bind()。
返回值成功则返回新的socket处理代码,失败返回-1,错误原因存于errno 中。
错误代码EBADF参数s非合法socket处理代码。 EFAULT参数addr指针指向无法存取的内存空间。 ENOTSOCK参数s为一文件描述词,非socket。 EOPNOTSUPP指定的socket并非SOCK_STREAM。 EPERM防火墙拒绝此连线。 ENOBUFS系统的缓冲内存不足。 ENOMEM核心内存不足。 5)connect(建立socket连线) 表头文件:
#include
Int connect(int sockfd,struct sockaddr * serv_addr,int addrlen);
函数说明connect()用来将参数sockfd的socket连至参数serv_addr指定的
网络地址。结构sockaddr请参考bind()。参数addrlen为sockaddr的结构长度。 返回值成功则返回0,失败返回-1,错误原因存于errno中。 错误代码EBADF参数sockfd非合法socket处理代码 EFAULT参数serv_addr指针指向无法存取的内存空间 ENOTSOCK参数sockfd为一文件描述词,非socket。 EISCONN参数sockfd的socket已是连线状态 ECONNREFUSED连线要求被server端拒绝。
ETIMEDOUT企图连线的操作超过限定时间仍未有响应。 ENETUNREACH无法传送数据包至指定的主机。 EAFNOSUPPORTsockaddr结构的sa_family不正确。
EALREADYsocket为不可阻断且先前的连线操作还未完成。
6)send()和recv()这两个函数用于面向连接的socket上进行数据传输。 函数定义:
Int send(int sockfd,const void * msg,int len,int flags);
参数:
Sockfd是你想用来传输数据的socket描述符; msg是一个指向要发送
数据的指针;Len是以字节为单位的数据的长度;flags一般情况下置为0(关于 该参数的用法可参照man手册)。
返回值:send()函数返回实际上发送出的字节数,可能会少于你希望发送的
数据。在程序中应该将send()的返回值与欲发送的字节数进行比较。当send() 返回值与len不匹配时,应该对这 种情况进行处理。 char*msg=\intlen,bytes_sent; ......
len=strlen(msg);
bytes_sent=send(sockfd,msg,len,0); ......
函数定义:int recv(int sockfd,void * buf,int len,unsigned int flags);
参数:sockfd是接受数据的socket描述符;buf是存放接收数据的缓冲区; len是缓冲的长度。Flags也被置为0。
返回值:Recv()返回实际上接收的字节数,当出现错误时,返回-1并置相应 的errno值。
sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。 由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地 址。
5、编译及运行
(1) 连接 PC 机和中央网关板的串口线;
(2) 打开 PC 机的超级终端,设置“115200,8,n,1”; (3) 连接 PC 机和中央网关板的网络线; (4) 设置 PC 机 IP 地址到”192.168.0.200”; (5) 给中央网关板加电;
(6) 使用 arm-linux-gcc 编译文件 socketclient.c 为 socketclient;
共分享92篇相关文档