当前位置: 技术问答>linux和unix
求教一些关于socket的系列知识,以及一点内核知识。
来源: 互联网 发布时间:2016-02-12
本文导语: 1)socket 通讯编程究竟指什么?其本质是什么?和具体的协议(tcp,udp,icmp,ip)有什么关系呢?为什么叫socket(插头)呢?socket_id 代表一个什么东东?应用程序中的SOCKET与内核是怎么交换数据的呢?现在的网卡都是全双工的...
1)socket 通讯编程究竟指什么?其本质是什么?和具体的协议(tcp,udp,icmp,ip)有什么关系呢?为什么叫socket(插头)呢?socket_id 代表一个什么东东?应用程序中的SOCKET与内核是怎么交换数据的呢?现在的网卡都是全双工的,用两个进程对一个socket_id实现同时发和收可以吗,就是一个发另一个收?请说述一下,最好能举个例子。
2)通过socket发送数据时,这些数据是被马上发出的吗?还是要放在缓存一会?比如使用数据报发送,也就是UDP协议。
3)操作系统的时间片和CPU的中断响应时间有什么关系?UNIX的时间片是100US,那是不是可以认为用POLL或者SELECT来做定时器,最多只能精确到100US?
谢谢你了。
2)通过socket发送数据时,这些数据是被马上发出的吗?还是要放在缓存一会?比如使用数据报发送,也就是UDP协议。
3)操作系统的时间片和CPU的中断响应时间有什么关系?UNIX的时间片是100US,那是不是可以认为用POLL或者SELECT来做定时器,最多只能精确到100US?
谢谢你了。
|
先简单说下 TCP 和 UDP
TCP(Transfer Control Protocol)传输控制协议是一种面向连接的协议,当我们的网络程序使用这个协议的时候,网络可以保证我们的客户端和服务端的连接是可靠的,安全的.
UDP(User Datagram Protocol)用户数据报协议是一种非面向连接的协议,这种协议并不能保证我们的网络程序的连接是可靠的,所以我们现在编写的程序一般是采用TCP 协议的
在linux 套接字编程是通过以下五个关键函数实现的:
socket
int socket(int domain, int type,int protocol)
domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX 和AF_INET 等).
AF_UNIX 只能够用于单一的Unix 系统进程间通信,
AF_INET 是针对Internet 的,因而可以允许在远程 主机之间通信
type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等)
SOCK_STREAM表明我们用的是TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流.
SOCK_DGRAM 表明我们用的是UDP 协议,这样只会提供定长的,不可靠,无连接的通信.
protocol:由于我们指定了type,所以这个地方我们一般只要用0 来代替就可以了
成功时返回文件描述符,失败时返回-1,看errno 可知道出错的详细情况.
bind [绑]
int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
sockfd:是由socket 调用返回的文件描述符.
addrlen:是sockaddr 结构的长度.
my_addr:是一个指向sockaddr 的指针. 在;中有 sockaddr 的定义
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in) 来代替.在;中有sockaddr_in 的定义
struct sockaddr_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
}
sin_family 一般为AF_INET,
sin_addr 设置为INADDR_ANY 表示可以 和任何的主机通信,
sin_port 是我们要监听的端口号.
sin_zero[8]是用来填充的
bind 将本地的端口同socket 返回的文件描述符捆绑在一起.成功是返回0,失败的情况和socket 一样
listen
int listen(int sockfd,int backlog)
sockfd:是bind 后的文件描述符.
backlog:设置请求排队的最大长度.
listen 函数将bind 的文件描述符变为监听套接字.返回的情况和bind 一样.
accept
int accept(int sockfd, struct sockaddr *addr,int *addrlen)
sockfd:是listen 后的文件描述符.
addr,addrlen 是用来给客户端的程序填写的,服务器端只要传递指针就可以了.
accept 成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了. 失败时返回-1
connect
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)
sockfd:socket 返回的文件描述符.
serv_addr:储存了服务器端的连接信息,其中sin_add 是服务端的地址
addrlen:serv_addr 的长度
connect 函数是客户端用来同服务端连接的.成功时返回0,sockfd 是同服务端通讯的文件
描述符 失败时返回-1;
TCP(Transfer Control Protocol)传输控制协议是一种面向连接的协议,当我们的网络程序使用这个协议的时候,网络可以保证我们的客户端和服务端的连接是可靠的,安全的.
UDP(User Datagram Protocol)用户数据报协议是一种非面向连接的协议,这种协议并不能保证我们的网络程序的连接是可靠的,所以我们现在编写的程序一般是采用TCP 协议的
在linux 套接字编程是通过以下五个关键函数实现的:
socket
int socket(int domain, int type,int protocol)
domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX 和AF_INET 等).
AF_UNIX 只能够用于单一的Unix 系统进程间通信,
AF_INET 是针对Internet 的,因而可以允许在远程 主机之间通信
type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等)
SOCK_STREAM表明我们用的是TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流.
SOCK_DGRAM 表明我们用的是UDP 协议,这样只会提供定长的,不可靠,无连接的通信.
protocol:由于我们指定了type,所以这个地方我们一般只要用0 来代替就可以了
成功时返回文件描述符,失败时返回-1,看errno 可知道出错的详细情况.
bind [绑]
int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
sockfd:是由socket 调用返回的文件描述符.
addrlen:是sockaddr 结构的长度.
my_addr:是一个指向sockaddr 的指针. 在;中有 sockaddr 的定义
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in) 来代替.在;中有sockaddr_in 的定义
struct sockaddr_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
}
sin_family 一般为AF_INET,
sin_addr 设置为INADDR_ANY 表示可以 和任何的主机通信,
sin_port 是我们要监听的端口号.
sin_zero[8]是用来填充的
bind 将本地的端口同socket 返回的文件描述符捆绑在一起.成功是返回0,失败的情况和socket 一样
listen
int listen(int sockfd,int backlog)
sockfd:是bind 后的文件描述符.
backlog:设置请求排队的最大长度.
listen 函数将bind 的文件描述符变为监听套接字.返回的情况和bind 一样.
accept
int accept(int sockfd, struct sockaddr *addr,int *addrlen)
sockfd:是listen 后的文件描述符.
addr,addrlen 是用来给客户端的程序填写的,服务器端只要传递指针就可以了.
accept 成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了. 失败时返回-1
connect
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)
sockfd:socket 返回的文件描述符.
serv_addr:储存了服务器端的连接信息,其中sin_add 是服务端的地址
addrlen:serv_addr 的长度
connect 函数是客户端用来同服务端连接的.成功时返回0,sockfd 是同服务端通讯的文件
描述符 失败时返回-1;
|
同意。
补充一下最后一个,答案也是没有关系。详细可以看看kernel/sched.c就明白了。
----------------
i_noname(晚九朝五) ( ) 信誉:100 Blog 加为好友 2007-6-17 18:28:46 得分: 0
个人理解,不保证正确性
1:
socket是TCP协议栈提供给用户的接口,就像open/read等,属于系统调用。socket_id其实就是一个file descriptor。应用层通过系统调用与内核进行数据交换。
2:
发送方式可以设置,TCP协议栈默认是使用缓存的,你可以设成不使用。
3:
时间片大小是根据进程调度策略来定义的,和CPU中断响应时间没啥关系。
不是
补充一下最后一个,答案也是没有关系。详细可以看看kernel/sched.c就明白了。
----------------
i_noname(晚九朝五) ( ) 信誉:100 Blog 加为好友 2007-6-17 18:28:46 得分: 0
个人理解,不保证正确性
1:
socket是TCP协议栈提供给用户的接口,就像open/read等,属于系统调用。socket_id其实就是一个file descriptor。应用层通过系统调用与内核进行数据交换。
2:
发送方式可以设置,TCP协议栈默认是使用缓存的,你可以设成不使用。
3:
时间片大小是根据进程调度策略来定义的,和CPU中断响应时间没啥关系。
不是
|
个人理解,不保证正确性
1:
socket是TCP协议栈提供给用户的接口,就像open/read等,属于系统调用。socket_id其实就是一个file descriptor。应用层通过系统调用与内核进行数据交换。
2:
发送方式可以设置,TCP协议栈默认是使用缓存的,你可以设成不使用。
3:
时间片大小是根据进程调度策略来定义的,和CPU中断响应时间没啥关系。
不是
1:
socket是TCP协议栈提供给用户的接口,就像open/read等,属于系统调用。socket_id其实就是一个file descriptor。应用层通过系统调用与内核进行数据交换。
2:
发送方式可以设置,TCP协议栈默认是使用缓存的,你可以设成不使用。
3:
时间片大小是根据进程调度策略来定义的,和CPU中断响应时间没啥关系。
不是
|
我也理解一下
socket编程实际上就是进程间编程,不仅仅指网络编程.
不管你使用的那种协议,当你发送/接收一个数据包时,实际上时调用的一个系统调用sys_socket(),此调用会根据你
的设置把数据包放入/取出内核缓冲区,由网卡的驱动程序根据情况来把数据转换成电平信号传送出去,你讲的
全双工/半双工一般是指的电平信号的发送/和接收,属于较低层的概念.
|
再给个简单例子,你自己结合着,理解下:
我也是刚刚学习的,建议你买本书看看:
/******* 服务器程序 (server.c) ************/
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
int main(int argc, char *argv[])
{
int sockfd,new_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size,portnumber;
char hello[]="Hello! Are You Fine?n";
portnumber=8888;
/* 服务器端开始建立socket 描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"Socket error:%sna",strerror(errno));
exit(1);
}
/* 服务器端填充 sockaddr 结构 */
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
/* 捆绑sockfd 描述符 */
if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{
fprintf(stderr,"Bind error:%sna",strerror(errno));
exit(1);
}
/* 监听sockfd 描述符 */
if(listen(sockfd,5)==-1)
{
fprintf(stderr,"Listen error:%sna",strerror(errno));
exit(1);
}
while(1)
{
/* 服务器阻塞,直到客户程序建立连接 */
sin_size=sizeof(struct sockaddr_in);
if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size
))==-1)
{
fprintf(stderr,"Accept error:%sna",strerror(errno));
exit(1);
}
fprintf(stderr,"Server get connection from %sn",
inet_ntoa(client_addr.sin_addr));
if(write(new_fd,hello,strlen(hello))==-1)
{
fprintf(stderr,"Write Error:%sn",strerror(errno));
exit(1);
}
/* 这个通讯已经结束 */
close(new_fd);
/* 循环下一个 */
}
close(sockfd);
exit(0);
}
/******* 客户端程序 client.c ************/
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
int main(int argc, char *argv[])
{
int sockfd;
char buffer[1024];
struct sockaddr_in server_addr;
struct hostent *host;
int portnumber,nbytes;
if((host=gethostbyname("localhost")==NULL)
{
fprintf(stderr,"Gethostname errorn");
exit(1);
}
portnumber=8888;
/* 客户程序开始建立 sockfd 描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"Socket Error:%san",strerror(errno));
exit(1);
}
/* 客户程序填充服务端的资料 */
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((struct in_addr *)host->h_addr);
/* 客户程序发起连接请求 */
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)
)==-1)
{
fprintf(stderr,"Connect Error:%san",strerror(errno));
exit(1);
}
/* 连接成功了 */
if((nbytes=read(sockfd,buffer,1024))==-1)
{
fprintf(stderr,"Read Error:%sn",strerror(errno));
exit(1);
}
buffer[nbytes]='';
printf("I have received:%sn",buffer);
/* 结束通讯 */
close(sockfd);
exit(0);
}
我也是刚刚学习的,建议你买本书看看:
/******* 服务器程序 (server.c) ************/
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
int main(int argc, char *argv[])
{
int sockfd,new_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size,portnumber;
char hello[]="Hello! Are You Fine?n";
portnumber=8888;
/* 服务器端开始建立socket 描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"Socket error:%sna",strerror(errno));
exit(1);
}
/* 服务器端填充 sockaddr 结构 */
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
/* 捆绑sockfd 描述符 */
if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{
fprintf(stderr,"Bind error:%sna",strerror(errno));
exit(1);
}
/* 监听sockfd 描述符 */
if(listen(sockfd,5)==-1)
{
fprintf(stderr,"Listen error:%sna",strerror(errno));
exit(1);
}
while(1)
{
/* 服务器阻塞,直到客户程序建立连接 */
sin_size=sizeof(struct sockaddr_in);
if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size
))==-1)
{
fprintf(stderr,"Accept error:%sna",strerror(errno));
exit(1);
}
fprintf(stderr,"Server get connection from %sn",
inet_ntoa(client_addr.sin_addr));
if(write(new_fd,hello,strlen(hello))==-1)
{
fprintf(stderr,"Write Error:%sn",strerror(errno));
exit(1);
}
/* 这个通讯已经结束 */
close(new_fd);
/* 循环下一个 */
}
close(sockfd);
exit(0);
}
/******* 客户端程序 client.c ************/
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
#include ;
int main(int argc, char *argv[])
{
int sockfd;
char buffer[1024];
struct sockaddr_in server_addr;
struct hostent *host;
int portnumber,nbytes;
if((host=gethostbyname("localhost")==NULL)
{
fprintf(stderr,"Gethostname errorn");
exit(1);
}
portnumber=8888;
/* 客户程序开始建立 sockfd 描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"Socket Error:%san",strerror(errno));
exit(1);
}
/* 客户程序填充服务端的资料 */
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((struct in_addr *)host->h_addr);
/* 客户程序发起连接请求 */
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)
)==-1)
{
fprintf(stderr,"Connect Error:%san",strerror(errno));
exit(1);
}
/* 连接成功了 */
if((nbytes=read(sockfd,buffer,1024))==-1)
{
fprintf(stderr,"Read Error:%sn",strerror(errno));
exit(1);
}
buffer[nbytes]='';
printf("I have received:%sn",buffer);
/* 结束通讯 */
close(sockfd);
exit(0);
}
|
1 用两个进程对一个socket_id实现同时发和收可以吗,当然可以的
2 不是,要到系统的缓冲区,然后再由系统发送出去的
2 不是,要到系统的缓冲区,然后再由系统发送出去的
|
socket不仅可以应用TCP协议,还可以应用UDP协议,只不过tcp是有连接的,udp是无连接的罢了。
至于他们的传输,应该说在我们的程序中就只调用send(),recv(),一类函数而已,我们自己的基本是没有做什么事情的.
至于他们的传输,应该说在我们的程序中就只调用send(),recv(),一类函数而已,我们自己的基本是没有做什么事情的.
|
对于上面那个服务器端程序,while(1),我感觉用这样的例子教人,不太好,难道就让这个程序独占系统资源,一直等在这里吗?虽然很多书上都举这样的例子,但是好像在实际中,没有人这样作的,如果你的服务器在等待客户端连接的时候,就像死机了一样,不能处理其它事情,我想,也没有客户会接受吧。
|
回楼上:没你想得那么严重
阻塞式的socket,accept如果不成功,该线程就会被阻塞,完全不占CPU,直到有连接进来,线程才会重新被唤醒,所以不会出现你想像中,像死机一样的情形。
阻塞式的socket,accept如果不成功,该线程就会被阻塞,完全不占CPU,直到有连接进来,线程才会重新被唤醒,所以不会出现你想像中,像死机一样的情形。