转自:socket的使用方法
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);
}