套接字在网络编程中的作用是什么?为什么称它为套接字?
P2,网络编程就是编写程序让两台联网的计算机相互交换数据。在我们不需要考虑物理连接的情况下,我们只需要考虑如何编写传输软件。操作系统提供了名为“套接字”,套接字是网络传输传输用的软件设备
socket英文原意是插座:我们把插头插到插座上就能从电网获得电力供给,同样,为了与远程计算机进行数据传输,需要连接到Internet,而变成中的“套接字”就是用来连接该网络的工具
在服务器端创建套接字后,会依次调用listen函数和accept函数。请比较并说明两者作用
listen:将套接字转为可接受连接方式
accept:受理连接请求,并且在没有连接请求的情况调用该函数,不会返回。直到有连接请求为止。二者存在逻辑上的先后关系
Linux中,对套接字数据进行I/O时可以直接使用I/O相关函数;而在Windows中则不可以。原因为何?
Linux把套接字也看作是文件,所以可以用文件I/O相关函数;而Windows要区分套接字和文件,所以设置了特殊的函数
创建套接字后一般会给它分配地址,为什么?为了完成地址分配需要调用哪些函数?
要在网络上区分来自不同机器的套接字,所以需要地址信息。分配地址是通过bind()
函数实现
Linux中的文件描述符与Windows的句柄实际上非常类似。请以套接字为对象说明他们的含义。
Linux的文件描述符是为了区分指定文件而赋予文件的整数值(相当于编号)。Windows的文件描述符其实也是套接字的整数值,其目的也是区分指定套接字。
底层文件I/O函数与ANSI标准定义的文件I/O函数之间有何区别?
ANSI标准定义的输入、输出函数是与操作系统(内核)无关的以C标准写成的函数。相反,底层文件I/O函数是直接提供的。理论上ANSI标准I/O提供了某些机制,性能上由于底层I/O
参考本书给出的示例low_open.c和low_read.c,分别利用底层文件I/O和ANSI标准I/O编写文件复制程序。可任意指定复制程序的使用方法
什么是协议?在收发数据中定义协议有何意义?
协议就是为了完成数据交换而定好的约定。因此,定义协议意味着对数据传输所必需的的承诺进行定义。
面向连接的TCP套接字传输特性有3点,请分别说明。
下面哪些是面向消息的套接字的特性?a、c、e
UDP 、TCP 、TCP
何种类型的套接字不存在数据边界?这类套接字接收数据时需要注意什么?
连接指向型TCP套接字不存在数据边界。因此输入输出函数的响应次数不具有意义。重要的不是函数的响应次数,而是数据的收发量。因此,必须将传输数据的量和接收数据的量制作成编码,保证发送数据的量和接收数据的量是一致的,特别要注意是制作依赖函数响应次数判断代码
修改代码
/*****************************tcp_serv.c*********************************/
#include
#include
#include
#include
#include
#include
#include
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[]="Hello World!";
if(argc!=2){
printf("Usage : %s \n", argv[0]);
exit(1);
}
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
clnt_addr_size=sizeof(clnt_addr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size);
if(clnt_sock==-1)
error_handling("accept() error");
write(clnt_sock, message, 4);
write(clnt_sock, message+4, 4);
write(clnt_sock, message+8, 4);
write(clnt_sock, message+12, sizeof(message)-12);
close(clnt_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
/*****************************tcp_clnt.c*********************************/
#include
#include
#include
#include
#include
#include
#include
void error_handling(char *message);
int main(int argc, char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len=0;
int idx=0, read_len=0, i;
if(argc!=3){
printf("Usage : %s \n", argv[0]);
exit(1);
}
sock=socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("connect() error!");
for(i=0; i<100; i++) // busy waiting!!
printf("Wait time %d \n", i);
read(sock, message, sizeof(message));
printf("Message from server: %s \n", message);
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
IP地址族IPv4和IPv6有何区别?在何种背景下诞生了IPv6?
IPV4是4字节地址族,IPV6是16字节地址族。IPV6的诞生是为了应对2010年前后IP地址耗尽的问题而提出的标准
通过IPV4网络ID、主机ID及路由器的关系说明向公司局域网中的计算机传输数据的过程
首先数据传输的第一个环节是向目标IP所属的网络传输数据。此时使用的是IP地址中的网络ID。传输的数据将被传到管理网络的路由器,接受数据的路由器将参照IP地址的主机号找自己保存的路由表,找到对应的主机发送数据
套接字地址分为IP地址和端口号。为什么需要IP地址和端口号?或者说,通过IP可以区分哪些对象?通过端口号可以区分哪些对象?
IP地址是为了区分网络上的主机。端口号是区分同一主机下的不同的SOCKET,以确保软件准确收发数据。
CAB
计算机通过路由器或交换机连接到互联网。请说出路由器和交换机的作用
路由器是帮助数据传输到目的地的中介。不仅如此,还起到帮助连接本地网络的电脑和互联网的作用
什么是知名端口?其范围是多少?知名端口中具有代表性的HTTP合同FTP端口号各是多少?
“知名端口(Well-known PROT)”是指预定分配给特定操作的端口。其范围是0~1023,其中最知名的端口是HTTP:80端口和TCP:21
题目大概意思是:为什么bind中第二个参数是sockaddr
,但是传入的是sockaddr_in
bind函数第二个参数类型是sockaddr
结构体,很难份分配IP地址和端口号,因此IP地址和PORT号的分配是通过sockaddr_in
完成的。因为该结构体和sockaddr
结构体的组成字节序和大小完全相同,所以可以强转
请解释大端序、小端序、网络字节序,并说明为何需要网络字节序
小端序是把高位字节存储到高位地址上;大端序是把高位字节存储到低位地址上。因为保存栈的方式有差异,所以对网络传输数据的过程制定了标准,这就是“网路字节序”。而且,在网络字节序中,数据传输的标准是“大端序”
大端计算机希望将4字节整型数据12传到小端序计算机。请说出数据传输过程中发生的字节序变换过程
因为网络字节序的顺序标准是“大端序”,所以大端序的计算机在网络传输中不需要先转换字节顺序,直接传输。但是接受数据的是小端序计算机,因此,要经过网络转本地序的过程,再保存到存储设备上
怎么表示回送地址?其含义是什么?如果向回送地址传输数据将会发生什么情况?
回送地址表示计算机本身,为127.0.0.1
。因此,如果将数据传送到IP地址127.0.0.1
,数据会不会通过传输到网络的其他设备上而直接返回。
请说明TCP/IP的4层协议栈,并说明TCP和UDP套接字经过的层级结构差异
链路层—>IP层—>TCP层—>应用层
链路层—>IP层—>UDP层—>应用层
请说出TCP/IP协议栈中链路层和IP层的作用,并给出两者关系。
链路层是LAN、WAN、MAN等网络标准相关的协议栈,是定义物理性质标准的层级。相反,IP层是定义网络传输数据标准的层级。即IP层负责以链路层为基础的数据传输
为何需要把TCP/IP协议栈分成4层(或7层)?结合开放式系统回答
将复杂的TCP/IP协议分层化的话,就可以将分层的层级标准发展成开放系统。实际上,TCP/IP是开放系统,各层级都被初始化,并以该标准为依据组成了互联网。因此,按照不同层级标准,硬件和软件可以相互替代,这种标准化是TCP/IP蓬勃发张的依据
客户端调用connect函数向服务器端发送连接请求。服务器端调用哪个函数后,客户端可以调用connect函数?
listen函数,客户端才可以调用connect函数
什么时候创建连接请求等待队列?它有何作用?与accept有什么关系
listen函数的调用创建了请求等待队列。它是存储客户端连接请求信息的空间。accept函数调用后,将从本地存储的连接请求信息取出,与客户端建立连接。
客户端中为何不需要调用bind函数分配地址?如果不调用bind函数,那何时、如何向套接字分配IP地址和端口号?
客户端是请求连接的程序,不是一个接收连接的程序。所以,指导服务器的地址信息是更重要的因素,没有必要通过bind函数明确地分配地址信息。但是,要想和服务器通信,必须将自己的地址信息分配到套接字上,因此,在connect函数调用时,自动把IP地址和端口号输入到套接字上
改成迭代服务器端
#include
#include
#include
#include
#include
#include
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[]="Hello World!";
if(argc!=2){
printf("Usage : %s \n", argv[0]);
exit(1);
}
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
if( bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1 )
error_handling("bind() error");
if( listen(serv_sock, 5)==-1 )
error_handling("listen() error");
clnt_addr_size=sizeof(clnt_addr);
while(1)
{
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size);
if(clnt_sock==-1)
break;
write(clnt_sock, message, sizeof(message));
close(clnt_sock);
}
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
请说明TCP套接字连接设置的三次握手过程。尤其是3次数据交换过程每次收发的数据内容。
网上有很多,就不一一写了
TCP是可靠的数据传输协议,但在通过网络通信的过程可能丢失数据。请通过ACK和SEQ说明TCP通过何种机制保证丢失数据的可靠传输。
SEQ顺序标识符是给信息编号。ACK是用于回复带有编号的信息。也就是说,每次传输信息时,都同时发送SEQ标识,而受到信息的主机应以SEQ信息为基础回复发送信息的主机。通过这种机制,传输数据的主机就可以确认数据是否被正确接收。在传输失败时,可以重新传送。
TCP套接字中调用write和read函数时数据如何移动?结合I/O缓冲进行说明
当write函数被调用时,数据就会向端口的输出缓冲区移动。然后经过网络传输传输到对方主机套接字的输入缓冲。这样,输入缓冲中存储的数据通过read函数的响应来读取
对方主机的输入缓冲剩余50字节空间时,若本方主机通过write函数请求传输70字节,问TCP如何处理这种情况?
对方主机会把输入缓冲中可存储的数据大小传送给要传输数据的数据(本方)。因此,在剩余空间为50字节的情况,即使要求传送70字节的数据,也不能传输50字节以上,剩余的部分保存在传输方的输出缓冲中,等待对方主机的输入缓冲出现空间。而且,这种交换缓冲多余空间信息的协议被称为滑动窗口协议
更改程序,使服务器端和客户端各传送1次字符串…
/**********************************sendrecv_serv.c***********************************/
#include
#include
#include
#include
#include
#include
#include
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock;
int clnt_sock;
int str_len, i;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char msg1[]="Hello client!";
char msg2[]="I'm server.";
char msg3[]="Nice to meet you.";
char* str_arr[]={msg1, msg2, msg3};
char read_buf[100];
if(argc!=2){
printf("Usage : %s \n", argv[0]);
exit(1);
}
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
clnt_addr_size=sizeof(clnt_addr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size);
if(clnt_sock==-1)
error_handling("accept() error");
for(i=0; i<3; i++)
{
str_len=strlen(str_arr[i])+1;
write(clnt_sock, (char*)(&str_len), 4);
write(clnt_sock, str_arr[i], str_len);
read(clnt_sock, (char*)(&str_len), 4);
read(clnt_sock, read_buf, str_len);
puts(read_buf);
}
close(clnt_sock);
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
/***************************************recvsend_clnt.c***************************/
#include
#include
#include
#include
#include
#include
#include
void error_handling(char *message);
int main(int argc, char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char msg1[]="Hello server!";
char msg2[]="I'm client.";
char msg3[]="Nice to meet you too!";
char* str_arr[]={msg1, msg2, msg3};
char read_buf[100];
int str_len, i;
if(argc!=3){
printf("Usage : %s \n", argv[0]);
exit(1);
}
sock=socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("connect() error!");
for(i=0; i<3; i++)
{
read(sock, (char*)(&str_len), 4);
read(sock, read_buf, str_len);
puts(read_buf);
str_len=strlen(str_arr[i])+1;
write(sock, (char*)(&str_len), 4);
write(sock, str_arr[i], str_len);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
/**********************************file_serv.c***********************************/
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sd, clnt_sd;
FILE * fp;
char buf[BUF_SIZE];
char file_name[BUF_SIZE];
int read_cnt;
struct sockaddr_in serv_adr, clnt_adr;
socklen_t clnt_adr_sz;
if(argc!=2) {
printf("Usage: %s \n", argv[0]);
exit(1);
}
serv_sd=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));
bind(serv_sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
listen(serv_sd, 5);
clnt_adr_sz=sizeof(clnt_adr);
clnt_sd=accept(serv_sd, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
read(clnt_sd, file_name, BUF_SIZE);
fp=fopen(file_name, "rb");
if(fp!=NULL)
{
while(1)
{
read_cnt=fread((void*)buf, 1, BUF_SIZE, fp);
if(read_cnt
#include
#include
#include
#include
#include
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sd;
FILE *fp;
char buf[BUF_SIZE];
char file_name[BUF_SIZE];
int read_cnt;
struct sockaddr_in serv_adr;
if(argc!=3) {
printf("Usage: %s \n", argv[0]);
exit(1);
}
printf("Input file name: ");
scanf("%s", file_name);
fp=fopen(file_name, "wb");
sd=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
serv_adr.sin_port=htons(atoi(argv[2]));
connect(sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
write(sd, file_name, strlen(file_name)+1);
while((read_cnt=read(sd, buf, BUF_SIZE))!=0)
fwrite((void*)buf, 1, read_cnt, fp);
fclose(fp);
close(sd);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}