第1章 – 理解网络编程和套接字
套接字( socket ):是网络数据传输用的软件设备,用来连接该网络。
1、服务器创建过程:
第一步:调用 socket 函数创建套接字。
第二步:调用 bind 函数分配IP地址和端口号。
第三步:调用 listen 函数转为可接收请求状态。
第四步:调用 accept 函数受理连接请求。
socket() 函数
#include <sys/socket.h>
int socket ( int domain, int type, int protocol );
功能:
创建套接字。(套接字是网络数据传输用的软件设备)
参数:
domain:一般写 AF_INET
type:类型,一般写 SOCK_STREAM
protocol:一般写 0
返回值:
成功时返回文件描述符,失败返回 -1
【注】sockaddr_in 结构体
一般用 sockaddr_in 来表示bind() 里面的参数sockaddr
struct sockaddr_in {
short int sin_family; /* 协议族,在socket编程中只能是AF_INET */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
struct in_addr {
unsigned long s_addr;
};
bind() 函数
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:
给创建好的套接字分配IP地址和端口号。
参数:
sockfd:文件描述符,为socket()生成
addr :sockaddr_in结构体型变量,因为addr是结构体指针,所以实参要用&。
addrlen:结构体变量长度
返回值:
成功返回 0, 失败返回-1
eg. struct sockaddr_in serv_addr;
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)
listen() 函数
#include <sys/socket.h>
int listen( int sockfd, int backlog );
功能:
把套接字转化成可接受连接的状态。
参数:
sockfd:套接字文件描述符
vacklog:连接队列的最大长度 eg. 5
返回值:
成功返回0, 失败返回 -1
accept() 函数
#include <sys/socket.h>
int accept( int sockfd, struct sockaddr *addr, socklen_t *addrlen );
功能:
接受连接请求。
参数:
sockfd:套接字文件描述符
addr:sockaddr_in结构体型变量 (此时定义的结构体变量用于客户端,bind()用于服务器)
addrlen:结构体变量长度
返回值:
成功返回文件描述符,失败返回 -1
2、客户端创建过程
第一步:调用 socket 函数创建套接字
第二步:调用 connect 函数向服务器端发送连接请求
connect() 函数
#include <sys/socket.h>
int connect( int cockfd, struct sockaddr *serv_addr, soc klen_t addrlen );
功能:
在客户端调用客户端套接字。
参数:
sockfd:套接字文件描述符
addr:sockaddr_in结构体型变量
addrlen:结构体变量长度
返回值:
成功返回0, 失败返回-1
3、Linux文件操作
文件描述符
这三个是系统固定的,所以自己创建文件的文件描述符一般是从3开始。
open() 函数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
功能:
打开文件以读写数据
参数:
pathname:文件名的字符串地址(一般用相对地址)
flags 类似于给的权限位
返回值:
-1 表示失败
正常情况下会返回文件描述符( >0 )
close() 函数
#include <unistd.h>
int close(int fd);
功能:
关闭文件
参数:
fd:需要关闭的文件或套接字的文件描述符。
返回值:
成功返回0, 失败返回-1。
write() 函数
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:
将数据写入文件。
参数:
fd:文件描述符
buf:要传输数据的缓冲地址值
count:缓冲区大小。
返回值:
成功 返回写入的字节数; 失败返回-1
read() 函数
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:
读取文件中的数据
参数:
fd:文件描述符
buf:要传输数据的缓冲地址值
count:缓冲区大小。
返回值:
成功 返回接收的字节数;失败返回-1
4、课后习题
1) 套接字在网络编程中的作用是什么?为什么称它为套接字? P2
网络编程就是编写程序让两台联网的计算机相互交换数据。在我们不需要考虑物理连接的情况下,我们只需要考虑如何编写传输软件。操作系统提供了名为“套接字”,套接字是网络传输用的软件设备。
socket英文原意是插座:我们把插头插到插座上就能从电网获得电力供给,同样,为了与远程计算机进行数据传输,需要连接到Internet,而编程中的“套接字”就是用来连接该网络的工具。
2) 在服务器端创建套接字后,会依次调用listen函数和accept函数。请比较并说明两者作用?
listen:将套接字转为可接受连接方式。
accept:受理连接请求,并且在没有连接请求的情况调用该函数,不会返回。直到有连接请求为止。二者存在逻辑上的先后关系
3) Linux中,对套接字数据进行I/O时可以直接使用I/O相关函数;而在Windows中则不可以。原因为何?
Linux把套接字也看作是文件,所以可以用文件I/O相关函数;而Windows要区分套接字和文件,所以设置了特殊的函数
4) 创建套接字后一般会给它分配地址,为什么?为了完成地址分配需要调用哪些函数?
要在网络上区分来自不同机器的套接字,所以需要地址信息。分配地址是通过bind()函数分配IP地址和端口号。
5) Linux中的文件描述符与Windows的句柄实际上非常类似。请以套接字为对象说明他们的含义?
Linux的文件描述符是为了区分指定文件而赋予文件的整数值(相当于编号)。Windows的文件描述符其实也是套接字的整数值,其目的也是区分指定套接字。
6) 底层文件I/O函数与ANSI标准定义的文件I/O函数之间有何区别?
ANSI标准定义的输入、输出函数是与操作系统(内核)无关的以C标准写成的函数。相反,底层文件I/O函数是直接提供的。理论上ANSI标准I/O提供了某些机制,性能上由于底层I/O