原创 你真的会socket编程吗(1)

2014-6-6 18:19 3751 23 25 分类: 通信 文集: 操作系统

最近的项目都围绕着TCP socket在进行着,VC下的socket,Linux下的socket,感觉很简单,但是却发现其实下面的几个同事对于socket这个东西还只是“会”,但并不一定懂。

 

然后和大家在会上讨论了才发现,其实大家都不会socket编程。上网一搜“socket编程”出现的大多就两个代码程序,一个服务器端用来接收N个字节,一个客户端用来连接服务器端并且发送N个字节。想必大多数人都是这么来学习和使用socket的吧。至此,开始记录下个人对socket编程在应用时的一些稍微深入些的知识。

 

当然,套接字有很多种,我们常用的是socket套接字,用于网络通信实现,最常用的也就是TCP/UDP的socket编程了。下面以Linux环境的TCP通信为例,说一说我所理解的socket编程。

首先觉得还是同样以那两个比较经典的程序来入门理解下socket编程。

服务器端:

……

#define portnumber 3333

int main(int argc, char *argv[])
{
    int sockfd,new_fd;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int sin_size;
    int nbytes;
    char buffer[1024];

    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
        exit(1);
    }

    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);
    if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
    {
        fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
        exit(1);
    }

    if(listen(sockfd,5)==-1)
    {
        fprintf(stderr,"Listen error:%s\n\a",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:%s\n\a",strerror(errno));
            exit(1);
        }
        fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));

        if((nbytes=read(new_fd,buffer,1024))==-1)
        {
            fprintf(stderr,"Read Error:%s\n",strerror(errno));
            exit(1);
        }         
        buffer[nbytes]='\0';
        printf("Server received %s\n",buffer);
       
        close(new_fd);
    }
    close(sockfd);
    exit(0);
}

从上段代码分析看来,其实服务器端实现的流程大概是:建立socket->填充socket需要的网络参数配置->绑定socket套接字->监听来自客户端的连接申请->接收客户端的连接申请,建立socket连接->读写数据。

 

下面我们在依次分析分析程序中用到的那些和socket有关的代码。

 

sockaddr_in结构体,它的作用是封装一些建立socket需要用到的参数,包括协议族、IP地址、端口号等,这些都和socket的建立息息相关。

再来看socket()这个函数,首先给出函数原型:

 

int socket(int domain, int type, int protocol);

 

第一个参数domain,协议族。本例中使用的是协议族是AF_INET是指ARPA因特网协议族,当然有了AF_INET6以后则表示IPV4协议族,而AF_INET6是指IPV6了。除此之外,还有很多在教材中不容易出现的协议族:AF_UNIX(有人说叫UNIX协议族,但其实确切的说,它指代的是文件系统协议族,从这里可以看出,原来socket不一定都是用来网络通信的),AF_ISO(顾名思义,ISO标准协议族),不同的Linux内核中支持不同的协议族,大家可以在头文件socket.h里面找到。

第二个参数type,类型。这里的类型指的是套接字类型,共分以下几种:流套接字SOCK_STREAM、数据报套接字SOCK_DGRAM、原始套接字SOCK_RAW、包套接字SOCK_PACKET等等。需要说明的是,这里的类型需和第一个参数的协议族对应起来,也就是说有些套接字是只能适用于某个或某些协议族的,好比以上四个套接字类型是适用在AF_INET这个协议族的。分别介绍下这四个套接字类型,很多人直接把SOCK_STREAM理解成TCP,SOCK_DGRAM理解成UDP,那是没问题的。而SOCK_RAW是一个IP层的套接字类型,我们在实现PING功能时就要用到这个套接字类型。SOCK_PACKET是一个链路层的套接字,相对底层一些,用的不多,但是如果你想实现一个用于ARP攻击的程序,它会帮助到你。

第三个参数protocol,协议。也就是具体用到的传输协议。例程中用的值是0,代表的是使用默认协议。那就需要解释一下系统怎么知道默认的协议是什么?答案来自于前两个参数,协议受限于协议族和类型。也就是说,大部分时候前两个参数定下来以后第三个参数可设置性就不那么强了,比如本例程中的,AF_INET和SOCK_STREAM设定后,就代表使用的是TCP协议了,就不能再把第三个参数又设成IPPOTO_UDP来使用UDP协议了。

返回值,如果执行成功则返回一个套接字描述符,例程中将它赋给了sockfd。如果失败的话则返回-1或INVALID_SOCKET。那这个描述符有什么用呢?首先个人理解的描述符就好比是C++下面的类指针。通俗的说,描述符代表了一个内存标识,我们通过这个描述符可以访问到一些相关的变量和函数。因为是内存中的标识,而且描述符是存放在进程的一个特殊列表中,所以它只能用于本进程,就算定义成全局变量也是一样的。

 

在执行socket函数后,建立了一个套接字并且得到了它的描述符sockfd,之后则是需要具体填充一个sockaddr_in,将协议族、地址、端口号都填充进去。这个sockaddr_in结构体有什么用呢?在于bind函数。

 

我们之前得到的sockfd描述符只是向操作系统、进程申请得到了部分空间,但并没有和实际的网络服务挂上钩。bind函数则是要将之前的套接字和实际的IP、端口、TCP网络层建立直接的联系。也就是说,在bind之前如果去访问sockfd描述符标识的部分参数或者调用部分函数,都没有任何的实际意义,并不会产生直接的网络动作。而bind之后,则会直接产生真实的网络动作了。

 

而之后的listen函数则是与与sockfd描述符相关联的函数,目的在于将该服务器socket挂出去接收客户端的连接申请。

对于服务器端,重点则是accept函数。可以看到,该函数通过之前的sockfd描述符得到了另一个新的new_fd描述符。而且在后续的read函数或者write函数所使用的描述符都是这个new_fd。这个如何理解?

 

饿了,回家吃饭先。明日继续,揭秘些关于socket有趣而真实的小知识。

 

文章评论2条评论)

登录后参与讨论

用户377235 2014-7-9 17:50

不要纠结于接口本身,用的好的是如何处理多并发数,资源共享排队这些。

用户610434 2014-6-16 11:44

只是个接口,填充一下结构体就可以了!不同的是同样是Socket编程,有人却用的很妙
相关推荐阅读
jlx_cuc 2014-05-16 18:44
操作系统-读书笔记(1)
最近在业余阅读时找到一本《自己动手编写操作系统》觉得不错,视角很新颖,是有关操作系统书籍中为数不多的看了不想睡觉的读本。   由于这本书的出版时间较早,书中引导盘还使用的是软盘,所以在跟随...
jlx_cuc 2014-05-08 17:13
“改变”后的一丝体会
有两个月没写博客了,现在正是开学之际,有必要对前面两个月的工作做做记录。   经历过从北京到苏州的工作地点转换,生活和工作上的体会和收获颇丰。到苏州来以后会觉得,原来的一些看法还是太过狭隘...
jlx_cuc 2014-05-08 17:13
最终我又不要脸的回来了——成为一名“管理者”后的心得
之前的很长一段时间都没再更新过这个博客,一直在EDN潜水,一直在做伸手党。原因有两个,一个是忙,一个是懒。 最终我还是不要脸的回来了。还是想以后好好把这个博客经营下去,也好等咱们的娃长大了给它...
jlx_cuc 2013-07-11 17:13
【博客大赛】写给“即将入学”的硕士研究生们
原本题目定的是《写给即将入学的硕士研究生们》,后来想了想便加上了双引号。先来说说这个双引号。   早在一个多月前全国考研的最后结果便出来了,对于考上研的同学们来说,马上就要开始自己的研究生...
jlx_cuc 2013-04-08 23:22
【博客大赛】老罗和他的锤子之我的见解
事先说明,个人感觉我的观点还算是中立观点,并不是要感叹老罗和锤子有多好,也不想批评它们是否一无是处。只是想在时隔这么就没来EDN,恰巧又遇到锤子的发布这个时间点,发表些个人的感想和见解。 ...
我要评论
2
23
关闭 站长推荐上一条 /2 下一条