socket【插座;接口】:其实是模拟打电话的过程,IP地址就是电话号码,socket就是电话拨通的一条线
socket有2种方式:
①面向连接的流方式
【对应的协议】:TCP (Transport Control Protocol)
像打电话一样,是需要建立通路确保连接才能发送和接收信息.
【特点】可靠,有重发机制.
【例子】FTP,Telent
②无连接的数据报文方式
【对应的协议】:UDP (User Datagram Protocol)
像寄快递一样,不一定先发货就先到,也不一定到了信息是齐全的.
【特点】速率高,无重发机制【例子】实时语音,实时图像转送,广播
VC环境
使用封装好了的WINSOCK API
第一步,初始化函数调用
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
/***原型是int PASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
//PASCAL 和 FAR已作废,可以不用详细了解. ***/
//第一个是用哪个版本的API,/WORD是一个无符号短整型,相当于一个汉字=2个英文字母=2字节.(取值范围0~216-1)
//第二个参数则是一个 指针类型,它是指向 WSAData类型的
//调用成功会返回0.
要使用winsock的API则必须通过这个函数才能使用端口.
之后要说的都是用版本2的
※顺便一说,如果编译出现了这个错误error LNK2001: unresolved external symbol _WSAStartup@8
只需要在 工程->设置-> 连接->对象/库模块 这一栏填入ws2_32.lib,点"确定",就OK了~
第二步,建立端口
SOCKET socket(int af, int type,int proctocol);
//af是address family(地址族) ,一般填AF_INET表示在Internet的socket
//type 就是类型, 流方式是SOCK_STREAM, 数据报文方式是SOCK_DGRAM
//第三个是传输协议,一般填0,表示缺省,让他自己默认去
//返回类型是SOCKET,是WINSOCK分配给的Socket编号,当指针用就可以了.
//建立失败则返回INVALID_SOCKET(残疾缺口)
做了这一步之后相当于你买了一个插座.
而这个插座是TCP还是UDP就看你的参数了
第三步,绑定要通信的程序,成为服务器
int bind(SOCKET s, strut_ockaddr_in* name, int namelen);
//s就是第二步成功买回的,不,是申请到的接口
//name 是指进程具体地址,就像具体电话号码一样,他的类型是一个结构体
//namelen就是位置的长度
IP地址就能确认要连的是哪台主机,而一个电脑又有多个程序,所以端口就用来确认是哪个程序
一个端口只能一个程序占用(当然TCP和UDP不冲突)
下面是地址结构体具体
struct sockaddr_in{
short sin_family; //地址族,同样是设为AF_INET;
unsigned short sin_port; //端口,取值0~65536,但是1024以下大多被占用了
struct in_add sin_addr; // IP地址,太长了,所以这里也是个结构体
char sin_zero[8]; //8个空字符..只是为了和SOCKADDR大小相同好处理(2+2+4+8=16)
};
下面是IP地址的结构体
struct in_addr{
union{ //结构联合体,可以理解为分组
struct{
unsigned char s_b1,s_b2, s_b3, s_b4;
}S_un_b;
struct{
unsigned short s_w1,s_w2;
}S_un_w;
unsigned long S_addr;
}S_un;
};
嗯哼,对于192.168.0.105
第一种赋值方式: s_b1=192, s_b2=168, s_b3=0, s_b4=105;第二种赋值方式:s_w1=(168<<8)|192; s_w2=(105<<8)|0;
第三种赋值方式:S_addr=(105<<24)|(0<<16)|(168<<8)|192;
你看上面,为啥看起来好像反转了一样,怎么算都是(105.0.168.192)啊
没错,这就关系到字节序的问题了.
除了JAVA使用BIG_ENDIAN,其他几乎都是使用little_endian的
IP地址的数据报头也是little_endian
到此,前三步就足够建立一个服务端了~(其实每一步都只是一句话而已啊行不行啊)
我总结下:最简单的建一个服务端,端口为4000
sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_addr.S_un.S_addr=INADDR_ANY; //INADDR_ANY:本机IP地址
m_addr.sin_port=htons(4000);
int a1=WSAStartup(2, &wsaData );
if(a1==0) //返回0则表示初始化成功
{
m_hSocket=socket(AF_INET, SOCK_STREAM ,0);
int a2=bind(m_hSocket, (LPSOCKADDR)&addr ,sizeof(addr));
}
而客户端的话,前两步就足够了,不需要绑定,只需要得到一个socket.
接下来到socket的功能,这里分为TCP和UDP
TCP
服务端等待连接
int listen(SOCKET s,int backlog);
//s就是申请到的socket
//backlog可取1~5,表示可以有多少个人排队
//返回值可以看是否有出错
这里就好比如打电话,如果对方关机,就是打不通,会返回SOCKET_ERROR
如果对方正在通话中,忙线,你就需要等待.而允许有1~5个人等待,其余就直接拒接.
客户端请求连接
int connect(SOCKET s,struct sockaddr_in *name, int *namelen );
服务端接受连接
SOCKET accept(SOCKET s,struct sockaddr_in *addr, int *addrlen);
//s是绑定的服务端接口,建立后返回的是一个SOCKET,是用来会话的.原先socket还要继续监听其他客户端请求连接
//*addr 是客户端的地址,可以填NULL,只是用来确认来自哪里的连接
发送消息
int send(SOCKET s, char *buf ,int len, int flags);
//s表示要发给谁的接口,如果服务端要发给某个客户端,则用accpet返回的那个
//buf 和 len 是数据包 和其大小. flag一般取0
//错误时可以返回SOCKETERROR
接收信息
int recv(SOCKET s, char *buf ,int len, int flags);
//s表示来信的接口,其余同上
UDP
发送消息
int sendto(SOCKET s, char *buf ,int len, int flags, struct sockaddr_in to, int tolen);
//s 是发送方申请到的SOCK_DGRAM端口
//*buf 和 len 是要发送的消息及其长度
//flags依然是0
//to 和 tolen是要发给哪个地址及其长度.(基本就是要发给一个绑定好的服务器).
看起来其实就是TCP的connect(....) + send(...)
接收消息
int sendto(SOCKET s, char *buf ,int len, int flags, struct sockaddr_in from, int *fromlen);
//s 是发送方申请到的SOCK_DGRAM端口
//*buf 和 len 是要接收的消息及其长度,如果文件长度大于len,多出的部分就会扔掉的
//flags依然是0
//from 和 *tofrom是收到信息后,可以保存信息的来源地址及其长度.就是说from只是一个指针,里面没东西的.
看起来就像是TCP的accept(...) + recv(...)
有阻塞模式和异步模式.
阻塞模式是指一指等待对方的请求连接或者回复,不然的话就动不了.
异步模式则是则各种搞,收到消息了再来处理.所以个人偏好异步模式.
上面所说的都是正常的阻塞模式下面说一下以异步模式
其实异步模式需要在申请了一个socket之后就去转换.
int WSAAsyncSelect(SOCKET s, HWND hwnd , unsigned int wMsg , long lEVENT);
//s就是要变为非阻塞的套接字
//hWnd是句柄, 何谓句柄,就是一个long而已,反正在MFC中就是用来标示每个元件的唯一ID.这里用于窗口的句柄,这样可以知道是该给哪个窗口处理信息
//wMsg是用户自定义的信息,就是遇到要发生的动作,就发送一个这样的信息,注意,信息需要定义#define ,还要是WM_USER+100开始
//lEvent 这个是期望要发生什么动作时处理信息
返回值是0时正常,其他均为错误.
具体用法因为要加上MFC的元件比较容易明白,不然就长篇大论了.
可以下载下面的代码来看
最后,关闭接口
closesocket(SOCKET s);
最后总结
其中红色的地方就是阻塞模式,要等待对方的回应
下面是用基于VC的MFC的TCP和UDP简单聊天室代码
点击下载