最近这段时间,一直都在学习套接字编程,下面就写写自己学习的一些相关知识,也算是学习笔记吧(这篇文章主要参考孙鑫老师的教学视频和相关文档)。
首先是关于WinSocket网络编程方面的:
一、套接字的类型:
主要有三类:1、流式套接字(SOCK_STREAM),主要提供面向连接、可靠地数据传输服务,数据能够无差错的无重复的发送,并且能够按顺序的进行接收,主要基于TCP协议实现的
2、数据报式套接字(SOCK_DGRAM),主要提供无连接的服务,数据包能够独立的进行发送,不提供无错保证,数据可能会丢失或者重复,并且接收的顺序比较混乱,这种类型主要基于UDP协议实现的,所以这种类型的即时性比较高。(聊天通讯软件一般都是采用这种形式实现的)
3、原始套接字(SOCK_RAW),这种套接字一定要在root下使用,可以接受到本机网卡上的数据帧或者数据包,对于监听网络的流量和分析是很有作用的。
二、基于TCP(面向连接)socket编程的程序编写流程:
1、服务器端程序流程
a、创建套接字(socket)。
b、将套接字绑定到一个本地地址和端口上(bind)。
c、将套接字设为监听模式,准备接受客户请求(listen)。
d、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)
e、用返回的套接字和客户端进行通信(send/recv)。
f、返回,等待另一个客户请求。
g、关闭套接字
2、客户端程序编写流程
a、创建套接字(socket)。
b、向服务器发出连接请求(connect)。
c、和服务器端进行通信(send/recv)。
d、关闭套接字。
服务器端程序编写流程:
1、创建套接字(socket).
SOCKET socket ( int af, //指定的地址族,对于TCP/IP协议的套接字,他只能是AF_INET(也可以写成PF_INET) int type, //指定socket类型,对于1.1版本的socket,它支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM指定产生数据报式套接字 int protocol //是与特定的地址族相关的协议,如果指定为0,那么系统会根据地址格式和套接字类别,自动选择一个合适的协议(推荐这么使用)
);
2、将套接字绑定到一个本地地址和端口上(bind).
int bind(
SOCKET s, //要绑定的套接字
const struct sockaddr FAR *name, //指定了该套接字的本地地址信息,这是一个指向sockaddr结构的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构通常会随使用的网络协议不同而不同
int namelen //指定该地址结构的长度
);
a、sockaddr结构的定义如下(可以用sockaddr_in结构替换sockaddr,以方便我们填写地址信息):
struct sockaddr {
u_short sa_family;
char sa_data[14];
};
struct sockaddr_in{
short sin_family;
unsigned short sin_port;
IN_ADDR sin_addr;
char sin_zero[8];
};
另外,sockaddr_in结构中的sin_addr成员的类型是in_addr,其结构定义如下:
struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; };
in_addr结构实际上是一个联合,通常利用这个结构将一个点分十进制格式的IP地址转换成u_long类型,并将结果赋给成员S_addr。
可以把IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或者接受数据。
b、inet_addr函数和inet_ntoa函数
多数情况下,每个机器只有一个IP地址,但是有的机器可能会有多个网卡,每个网卡都可以有自己的IP地址,用INADDR_ANY可以简化程序的编写,将地址指定为INADDR_ANY,将允许一个独立应用接受发自多个接口的回应。如果我们只想让套接字使用多个IP中的一个地址,就必须指定实际的地址,要做到这一点,可以用inet_addr函数来实现,该函数的原型声明如下:
unsigned long inet_addr (const char *cp );
inet_addr函数需要一个字符串作为其参数,该字符串指定了以点分十进制格式表示的IP地址。而且inet_addr函数会返回一个适合分配给S_addr的u_long类型的数值。
inet_ntoa函数会完成相反地转换,他接受一个in_addr结构体类型的参数并返回一个以点分十进制格式表示的IP地址字符串。该函数的原型声明如下:
char * inet_ntoa (struct in_addr in );
3、将套接字设为监听模式,准备接受客户请求(listen)
listen函数的作用是将指定的套接字设置为监听模式。其声明原型如下:
int listen ( SOCKET s, //套接字描述符 int backlog 等待连接队列的最大长度 );
对于第二个参数backlog,如果设置为SOMAXCONN,那么下层的服务提供者将负责将这个套接字设置为最大的合理值。要注意的是,设置这个值是为了设置等待连接队列的最大长度,而不是在一个端口上同时可以进行连接的数目。例如,如果将backlog参数设置为2,当有3个请求同时到来时,前两个连接请求就会被放到等待请求连接队列中,然后由应用程序依次为这些请求服务,而第3个连接请求就被拒绝了。
4、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
SOCKET accept( SOCKET s, //套接字描述符,该套接字已经通过listen函数将其设置为监听状态 struct sockaddr *addr, // 指向一个缓冲区的指针,该缓冲区用来接受连接实体的地址,也就是当客户端向服务器发起连接,服务器接受这个连接时,保存发起连接的这个客户端的IP地址信息和端口信息 int *addrlen //也是一个返回值,指向一个整型的指针,返回包含地址信息的长度 );
未完待续。。。