Chinaunix首页 | 论坛 | 博客
  • 博客访问: 86615
  • 博文数量: 22
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 13
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-25 12:38
文章分类
文章存档

2016年(5)

2013年(17)

我的朋友

分类: 系统运维

2013-11-03 00:00:18

原文地址:Socket的使用方法 (windows) 作者:bo_00

Socket的概要
Socket就是为了向Internet连接请求的机构。支持网络通信的服务器和客户端都必须具备Socket。

下面是Socket的实际使用方法,分别说明服务器端的做成和客户端的做成。

另外,在此说明的内容的开发环境假想为Windows,现在Unix下使用Socket的话,请参考其它文档。
使用Socket的时候,一定要注意的地方,再[注意!]中记入了。编程时,请一定阅读。


服务器的做成方法(TCP)
在此对已TCP为BASE的服务器的构成方法进行说明。图1:是做成服务器的基本流程。

在此部分,对初始化时必要的函数socket, bind, listen, accept、以及结束处理的closesocket函数进行说明。

(图1)服务器做成流程
基本的流程如下:
1.socket函数
 制作为了受理的request的Socket。
2.bind函数
 将服务器的地址信息(IP地址以及 端口号)结合到Socket上。
3.listen函数
 设定request的连接等待队列、并准备连接要求。
4.accept函数
 受理连接等待的request、确立连接、并做成新的Socket。
  被由accept函数而新生成的Socket是通信专用的Socket、并且能由Send函数和Recv函数进行数据的发送和接收。
这些函数,因为再客户端程序中也被使用,所以稍后说明。


Socket的做成(Socket)
为了做成TCP服务器、首先必须做成供request被受理使用的Socket。
Socket函数的定义
SOCKET s=socket(int af,int type,int protocol);
 
 af  address family(地址簇)。通常是AF_INET
 type 通信protocol。设定是TCP通信、UDP通信。有如下参数:

SOCK_STREAM(以TCP进行通信)
SOCK_DGRAM(以UDP进行通信)
 protocol  对应address family(地址簇)固有的协议。通常为0
 返回值 成功的时候, 返回Socket
失败的时候, 返回INVALID_SOCKET(-1)


Socket函数的使用方法
生成TCP的Socket时、通常像下记方式记述。 
    SOCKET s=socket(AF_INET,SOCK_STREAM,0);
   if(s==INVALID_SOCKET){
       printf("Socket不能被生成 \n");
       exit(1);
   }
   /* 以下、继续进行bind函数 */
关于Socket的生成,上记完了。

结合地址信息(bind)
bind函数是向由Socket函数做成的Socket结合服务器的IP地址和端口号的函数。
因为bind函数用SOCKADDR_IN结构体接收这些信息,所以首先定义SOCKADDR_IN结构体。
SOCKADDR_IN结构体  
struct sockaddr_in{
    short sin_family;
    unsigned short sin_port;
    struct in_addr sin_addr;
    char sin_zero[8];
};
typedef sockaddr_in SOCKADDR_IN;

 sin_family  address family(地址簇)。AF_INET
 sin_port  端口号
 sin_addr  IP地址
 sin_zero  Padding 填充


bind函数的定义
int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);
 s  Socket
 name  向SOCKADDR_IN结构体内,已分配了地址信息的SOCKADDR结构体
 namelen  SOCKADDR_IN结构体Size。sizeof(SOCKADDR_IN)
 返回值  成功时0。
 失败时SOCKET_ERROR(-1)


bind函数的使用方法 
SOCKADDR_IN sain;
   sain.sin_family=AF_INET;
   sain.sin_addr.s_addr=INADDR_ANY;
   sain.sin_port=htons(port_number); 
   if(!bind(s,(SOCKADDR*)&sain,sizeof(sain)){
       printf("bind失败 \n");
       exit(1);
   }
   /* 以下、继续进行listen函数 */

向Socket结合地址信息的内容,上记完了。
另外,上记内容中出现的htons函数是将16位的主机字符序转换为网络字符序。
从little endian → big endian转换。


request的收信待机(Listen)
Listen函数是开始进行request的收信待机的函数。
连接要求的有的request在连接被服务器确立之前变成了待机状态,但是listen函数管理变成这种待机状态的request。

listen函数定义
int listen(SOCKET s,int backlog);
 
 s  Socket
 backlog  存储待机状态的request的队列size
 返回值  成功:0
 失败:SOCKET_ERROR(-1)


listen函数的使用方法
if(!listen(s,10)){
       printf("listen失败 \n");
       exit(1);
   }
   /*以下、继续进行accept函数 */


确立接续(accept)

accept函数是从等待接续的request中取出一个,并确立连接的函数。
一旦确立连接,新的Socket就被做成。和客户端的通信就是用那个新的Socket。

accept函数的定义
SOCKET t=accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);

 s  socket
 addr  存储连接方向的地址信息
 addrlen  addr的Size。(必须)
 返回值  成功:新的socket。
 失败:INVALID_SOCKET(-1)。


accept函数的使用方法
SOCKET t;
   SOCKADDR_IN ta;
   int ta_len;
   ta_len=sizeof(ta);
   t=accept(s,&ta,&ta_len);
   if(t==INVALID_SOCKET){
       printf("不能确立连接 \n。");
       exit(1);
   }
   /* 以下、能够使用send函数和recv函数送受信 */
等待接续的队列中没有request时、一实行accept函数,程序就停在这里、然后一旦有接续要求,就再从这里开始运行。


关闭Socket(closesocket)
closesocket函数是切断通信,然后关闭Socket的函数。
closesocket函数定义
int closesocket(SOCKET s);
成功:0 、失败:SOCKET_ERROR
和close(SOCKET s);概念一样。




客户端的做成方法(TCP)
这部分对TCP客户端的构成方法进行说明。图1:是做成客户端的基本流程。 
已对socket函数进行了说明,所以在此主要对connect函数进行说明。
另外对在服务器的构成方法中未说明的send和recv函数进行说明。

图2做成客户端的流程


发出连接要求,并连接(connect)
connect函数向服务器端发送连接要求,并确定连接。
connect函数的定义
int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);

 s  socket
 name  向SOCKADDR_IN结构体内,已分配了连接目标的地址信息的SOCKADDR结构体
 namelen  SOCKADDR_IN结构体的size。sizeof(SOCKADDR_IN)
 返回值  成功:0
 失败:SOCKET_ERROR(-1)


connect函数的使用方法
struct hostent* h;
   SOCKADDR_IN sa;
   h=gethostbyname("hostname");
   if(!h){
       printf("无法发现主机 \n");
       exit(1);
   }
   sa.sin_family=AF_INET;
   sa.sin_addr.s_addr=h->h_addr;
   sa.sin_port=htons(port_number); 
   if(!connect(s,(SOCKADDR*)&sa,sizeof(sa)){
       printf("不能确立连接 \n");
       exit(1);
   }
   /* 以下、能够使用send函数和recv函数送受信*/
在这里、gethostbyname函数向DNS服务器询问,执行从domain取得IP地址的操作。一执行gethostbyname函数,就向hostent结构体的指针返回。
因为在这个结构体中有连接方向的IP地址,所以利用它,来做成地址信息。


数据发送(send)
在服务器端根据accept函数建立被连接的socket、在客户端通过connect函数建立被连接的socket。
send函数以及接下来将说明的recv函数是通过这样做而被确立连接的socket,来发送接收数据的函数。
在此对send函数进行说明。
send函数的定义
int send(SOCKET s,const char FAR* buf,int len,int flags);

 s  socket
 buf  发送的数据的指针
 len  发送的数据的长度
 flags  参数变量  通常为0
 返回值  成功:完成送信数据的长度(byte字节)
 失败:SOCKET_ERROR(-1)


send函数的使用方法
例如:发送""Hello\r\n"的时候,如下

int ok;
   const char* st="Hello\r\n";
   ok=send(s,st,7,0);
   if(ok==SOCKET_ERROR){
       printf("无法发送 \n");
       exit(1);
   }


数据接受(recv)
recv函数是执行接受数据的函数。通过recv函数能够接收被发送出来的数据。
recv函数的定义
int recv(SOCKET s,char FAR* buf,int len,int flags);

 s  socket
 buf  接收buf的指针
 len  接收buf的长度
 flags  参数变量 通常0
 返回值  成功:返回接收数据的长度。(1~len) 
 socket被关闭的时候: 返回 0 
 失败:SOCKET_ERROR(-1)


recv函数的使用方法
recv函数接收的数据不一定达到限定长度。
下记使用recv函数,并介绍接收达到限定长度的receive函数。
int receive(SOCKET s,char* buf,int* len){
    int revd_size;
    int tmp;
    revd_size=0;
    while(revd_size*len){ 
        tmp=recv(s,buf+revd_size,*len-revd_size,0);
        if(tmp==SOCKET_ERROR){ /* 发生Error */
            *len=received; 
            return SOCKET_ERROR;
        }
        if(tmp==0){ /* socket被切断*/
            *len=received; 
            return 0;
        }
        received+=tmp;
    }
    *len=received; 
    return received;
}
这个recevie函数,是否接收到限定长度,直到socket被关闭将连续呼出recv函数。
返回值同recv函数一样。
但是,接收途中socket被关闭的时候,自变量len作为指针,返回到此刻所接收到的数据的长度。


使用socket时的注意事项
使用socket时,有几个必须注意的地方。
那是include文件和库的指定,及socket库的初始化。

include文件和库
include文件是、库是ws2_32.lib

socket库的初始化
使用socket关联的函数前,执行下面的初始化的操作是必要的。
WORD wVersionRequested = MAKEWORD(2,2);
    WSADATA wsaData[1];
    if(WSAStartup(wVersionRequested,wsaData)){
        printf("socket库的初始化失败。");
        exit(1);
    }



阅读(2830) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~