socket知识
有了IP地址,socket可知道是与哪一台主机的哪一个进程通信
有了端口号,就知道是这个进程的哪一个套接字进行传输
应用进程使用描述符与它的套接字进行通信,也就是说一个进程创建一个套接字时就会返回一个套接字描述符
socket的地址信息最重要
已定义了结构sockaddr_in
:,使用TCP/IP协议族的网络应用程序声明端点地址变量时,使用结构sockaddr_in
struct sockaddr_in
{u_char sin_len; //地址长度u_char sin_family; //地址族 TCP/IP的地址是AF_INETu_short sin_port; //端口号struct in_addr sin_addr; //IP地址char sin_zero[8]; //未用
}
socket api(WinSock)
windows环境下,先使用WSAStartup
初始化Windows Sockets API
,然后应用程序执行任务,最后执行WSACleanup
释放所使用的Windows Sockets DLL
占用的系统资源,接触与socket库的绑定。
WSAStartup
int WSAStartup(WORD wVersionRequested,LPWSADATA IpWSAData);
使用Socket的应用程序在使用Socket之前必须首先调用WSAStartup函数
两个参数:
-
第一个参数指明程序请求使用WinSock版本,高位字节指明副版本、低位字节指明主版本
- 0x102表示2.1版本
-
第二个参数返回实际的WinSock的版本信息
- 指向WSADATA结构的指针
例:使用2.1版本WinSock的程序代码段
wVersionRequested = MAKEWORD(2,1);
err = WSAStartup(wVersionRequested,&wsaData);
socket
该函数创建套接字
sd = socket(protofamily,type,proto);
-
创建套接字
-
操作系统返回套接字描述符(sd)
-
第一个参数(协议族):protofamily = PF_INET (TCP/IP)
-
第二个参数(套接字类型):
type = SOCK_STREAM,SOCK_DGRAM or SOCK_RAW(TCP/IP)
-
第三个参数(协议号):0为默认
例:创建一个流套接字的代码段
struct protoent *p;
p = getprotobyname("tcp");
SOCKET sd = socket(PF_INET,SOCK_STREAM,p->p_proto);
不同类型的socket用于应用层与下层不同协议进行沟通。需要注意的是原始套接字SOCK_RAW需要拥有比较高的权限
TCP提供的服务特点:
可靠、面向连接、字节流传输、点对点
UDP提供的服务特点:
不可靠、无连接、数据报传输
Closesocket
int closesocket(SOCKET sd);
-
关闭一个描述符为sd的套接字
-
如果多个进程共享一个套接字,调用
closesocket
,将套接字引用计数减1,减至0才关闭 -
一个进程中的多线程对一个套接字的使用无计数
- 如果进程中的一个线程调用
closesocket
将一个套接字关闭,该进程中的其他线程也将不能访问该套接字
- 如果进程中的一个线程调用
-
返回值:0成功,SOCKET_ERROR不成功
bind
套接字创建的时候可能并没有地址 ,可以调用bind函数产生一个地址,或者说是用来绑定套接字的本地端点地址
int bind(sd,localaddr,addrlen);
端点地址其实就是IP地址 + 端口号
-
参数
- 套接字描述符 sd
- 端点地址 localaddr,结构为sockaddr_in
- 地址长度
-
客户程序一般不必显式调用bind函数,因为操作系统会帮助我们设置客户端的端点地址
-
对于服务器端需要调用,用来指定熟知端口号以及IP地址。如果服务器主机有多个网卡,产生多个ip地址,需要使用地址通配符,INADDR_ANY。表示在服务器运行的主机上任何一个可用ip地址都是可以用来传输的
listen
int listen(sd,queuesize);
-
置服务器端的流套接字处于监听状态
- 仅服务器端调用
- 仅用于面向连接的流套接字
-
设置连接请求队列(缓存)大小
-
返回值: 成功为0,失败为SOCKET_ERROR
connect
connect(sd,saddr,saddrlen);
- 客户程序调用connect函数来使客户套接字sd与特定计算机的特定端口saddr的套接字服务进行连接
- 仅仅用于客户端
- 可用于TCP客户端也可以用于UDP客户端
- TCP客户端:建立了TCP连接
- UDP客户端:只指定服务器端点地址
accept
newsock = accept(sd,caddr,caddrlen);
-
服务程序调用accept函数从处于监听状态的流套接字sd的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道
- 仅仅用于TCP套接字
- 仅仅用于服务器
-
利用新创建的套接字newsock与客户通信
为什么要这样做呢?因为TCP是点对点的,说明只能连接客户端和服务器的两个套接字。如果不这么做,就会导致TCP服务器在同一时刻只能为某一个客户提供服务,不能实现并发了。
send,sendto
send(sd,*buf,len,flags);
sendto(sd,*buf,len,flags,destaddr,addrlen);
- send函数用于TCP套接字(客户与服务器)或调用了connect函数的UDP客户端套接字
- sendto函数用于UDP服务器端套接字与为调用connect函数的UDP客户端套接字
recv,recvfrom
与上面的send、sendto对应
recv(sd,*buffer,len,flags);
recvfrom(sd,*buf,len,flags,senderaddr,saddrlen);
- recv函数从TCP连接的另一端接收数据,或者从调用了connect函数的UDP客户端套接字接收服务器发来的数据
- recvfrom函数用于从UDP服务器套接字与未调用connect函数的UDP客户端套接字接收对端数据
setsockopt,getsockopt
int setsockopt(int sd,int level,int optname,*optval,int optlen);
int getsockopt(int sd,int level,int optname,*optval,int socklen_t* optlen);
- setsockopt()函数用来设置套接字sd的选项参数
- getsockopt()函数用来获取任意类型、人影状态的套接口的选项当前值,并吧结果存入optval