Socket网络编程无论是什么平台,都有相似流程;在windows中我们一般使用WinSock2来进行网络通信,当然还有其他网络通信库。
今天具体说下TCP/IP Server的编程流程,具体分为6大步,下面一一解释!
我们需要头文件和库:
#include // Windows Socket Version 2.2
#pragma comment(lib,"ws2_32.lib") // Windows Socket Version 2.2 [32 bit]
1、初始化 Windows Socket
WSADATA wsa_data; //用来获取WSA的版本
int ret = 0;
ret = WSAStartup(MAKEWORD(2,2),&wsa_data); //返回值为0,不等于0 则出错,不过一般不会出错
MAKEWORD(2,2)转化成WSA可以识别的版本Version 2.2
如果返回成功我们也可以通过
LOBYTE(wsa_data,wVersion)
HIBYTE(wsa_data,wVersion)
来已经设置的版本,wVersion是在WinSock2.h里面定义的,所以这里我们不必纠结;
如果出错可以用WSAGetLastError()来获取出错代码,下面也可以同样用这种方法!
2、创建 Socket (套接字)
SOCKET fd_socket; //作为Server的套接字
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建套接字,如果出错,返回INVALID_SOCKET
如果出错,记得释放已经初始化的socket:WSACleanup();
socket的第一个参数为域;
域 |
描述 |
AF_INET |
IPv4 因特网域 |
AF_INET6 |
IPv6因特网域 |
AF_UNIX |
UNIX域 |
AF_UNSPEC |
未指定 |
我们常用的就是前两个,想了解更多请看《UNIX环境高级编程(APUE)》
socket的第二个参数也有四种:
Type |
Description |
SOCK_DGRAM |
长度固定的,无连接的不可靠报文传递 |
SOCK_RAW |
IP协议的数据报接口(POSIX.1中为可选) |
SOCK_SEQPACKET |
长度固定、有序、可靠的面向连接报文传递 |
SOCK_STREAM |
有序、可靠、双向的面向连接字节流 |
SOCK_DGRAM + IPPROTO_UDP 是UDP/IP的报文类型
SOCK_STREAM + IPPROTO_TCP 是TCP/IP的报文类型
其实我们最后一个参数可以用0代替,0 :default
3、绑定 Socket (套接字)与服务器IP/port
SOCKADDR_IN server_addr; //服务器配置
int port = 3200; //端口号
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); //端口号范围: 0 ~65535
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //INADDR_ANY意思是接受所有IP的连接
ret = bind(fd_socket,(SOCKADDR *)&server_addr,sizeof(SOCKADDR)); //绑定ip域socket,出错返回SOCKET_ERROR
如果出错,记得释放已经初始化的socket:WSACleanup();同时还要关闭socket:closesocket(fd_socket);
htons (host to network short)本机字节转化成网络字节,一般用于端口转化,因为端口是Short型的,server_addr.sin_port = htons(3400);
ntohs (network to host short)网络字节转化为本机字节,ntohs(server_addr.sin_port)
htonl (host to network long)一般用于IP转化,参数常用的是INADDR_ANY
inet_ntoa(server_addr.sin_addr) 把网络ip转化为字符串ip,如 “192.168.1.2”
inet_addr(char *ip) 把字符串ip转化成网络ip,eg: server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");这里表示服务器只接受127.0.0.1的连接;
4、服务器开始监听
ret = listen(fd_socket,5); //5代表最多运行有5个用户在排队,当然也可以设置很多个用户,不过经验证明不要设置太大为好!如果出错返回:SOCKET_ERROR
5、接受客户端的连接
SOCKADDR_IN client_addr; //存储客户端信息
SOCKET fd_client; //客户端套接字
fd_client = accept(fd_socket,(SOCKADDR *)&client_addr,sizeof(SOCKADDR)); //错误返回:INVALID_SOCKET
如果成功,我们就可以开始与客户端进行通信了,如果要与多个客户端进行长时间通信,在UNIX、LINUX里面我们可以用创建进程来处理客户端!
但是在windows环境编程里面,我们用进程来一一处理,后面我会把windows进程编程更新出来!
6、服务器与客户端的通信
6.1 获取客户端的信息
char * inet_ntoa(client_addr.sin_addr); 将网络字节转化为字符串
int ntohs(client_addr.sin_port) 将网络字节转化为本机short字节
inet_ntoa获取本机客户端ip,ntohs获取客户端连接端口
6.2 读取客户端发来的信息
char buff[1024];
memset(buff,0x0,1024); //将buff全部置0
ret = recv(fd_client,buff,1024,0); //1024为buff的size,最后一个参数0是非阻塞模式,出错返回:SOCKET_ERROR
当然,这里我们也可以设置为阻塞模式;
6.3 发送数据到客户端
char send_buff[1024];
memset(send_buff,0x0,1024);
strcpy(send_buff,"xxxxxxxxxxxxxx");
ret = send(fd_client,send_buff,strlen(send_buff),0); //同样,这里的0是非阻塞模式
6.4 关闭客户端套接字
closesocket(fd_client);
如果数据处理完成,不想跟客户端再进行通信了,我们就关闭fd_client;
7、关闭服务器并释放套接字
closesocket(fd_socket);
WSACleanup();
也可以一直重复5.6步!
PS:希望大家多多指点!