socket通信流程:
1、服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket
2、服务器为socket绑定ip地址和端口号
3、服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开
4、客户端创建socket
5、客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket
6、服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端谅解请求
7、客户端连接成功,向服务器发送连接状态信息
8、服务器accept方法返回,连接成功
9、客户端向socket写入信息
10、服务器读取信息
11、客户端关闭
12、服务器端关闭
使用到的函数:bind() listen() accept() connect() recv() send() recvform() sendto()
int bind(int fd, const struct sockaddr* my_addr, socklen_t addrlen);//成功返回0,失败返回-1,
例:if(bind(sockClient,(LPSOCKADDR)&local,sizeof(local) == SOCKET_ERROR)
int listen(int fd, int backlog);//listen()成功返回0,失败返回-1
例:if(listen(sockSrv, 10) == SOCKET_ERROR) //backlog 设置的数量
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//失败返回-1
例:SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrClient, &len);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//成功返回0,失败返回-1
例:if (connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET)//#define INVALID_SOCKET (SOCKET)(~0)非0
int send( SOCKET s, const char FAR *buf, int len, int flags );//socket ,缓冲区 ,长度,0
例:if(send(sockConn, buf, sizeof(buf), 0)==0)//第四个参数一般置0。
int recv( SOCKET s, char FAR *buf, int len, int flags );
例:int num = recv(sockClient, buff, sizeof(buff), 0);
int sendto(int sockfd, const void *msg,int len unsigned int flags, const struct sockaddr *to, int tolen);
例:sendto(sockClient, szSrvMsg, sizeof(szSrvMsg), 0, (SOCKADDR*)&addrClnt, nLen);
int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *from,int *fromlen);
例:if (SOCKET_ERROR != recvfrom(sockClient, szMsg, sizeof(szMsg), 0, (SOCKADDR*)&addrSrv, &nLen))
sockaddr_in结构体:
struct sockaddr { //sockaddr是给操作系统使用
unsigned short sa_family; // 2 bytes address family, AF_xxx,sa_family是地址家族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET,代表TCP/IP协议族
char sa_data[14]; // 14 bytes of protocol address
};
typedef struct sockaddr_in { //程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口
short sin_family; // 2 bytes e.g. AF_INET, AF_INET6
//sockaddr用其余14个字节来表示sa_data, sockaddr_in把14个字节拆分成sin_port, sin_addr和sin_zero
unsigned short sin_port; // 2 bytes e.g. htons(3490), 端口号
IN_ADDR sin_addr; //struct in_addr sin_addr; // 4 bytes see struct in_addr, below, IP 地址
char sin_zero[8]; // 8 bytes zero this if you want to, sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小
}SOCKADDR_IN;
typedef 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;
#define s_addr S_un.S_addr
} IN_ADDR;
例:SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET;
server_addrl.sin_port = htons(1048);
server_addr.sin_addr.s_addr = inet_addr(INADDR_ANY); 等价于 server_addr.sin_addr.S_un.S_addr = inet_addr(INADDR_ANY);
(1)socket创建区别:
TCP: SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
UDP: SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);
创建socket套接字 int socket(int domain, int type, int protocol);
domain指哪一种地址类型;PF_INET, AF_INET: Ipv4网络协议; PF_INET6, AF_INET6: Ipv6网络协议。
Windows socket只支持一个通信区域:AF_INET国际网区域;
type参数是设置通信的协议类型:SOCK_STREAM(有保障)或SOCK_DGRAM(无保障);
参数protocol指socket使用的传输协议编号,一般设置为0.
(2) 加载socket动态链接库(dll) 初始化套接字库
WORD wVersionRequested;
WSADATA wsaData; // 这结构是用于接收Wjndows Socket的结构信息的
int err;
wVersionRequested = MAKEWORD( 2, 2 ); // 请求1.1版本的WinSock库
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
Printf(""加载套接字失败:%d......\n",WSAGetLastError());
return -1; // 返回值为零的时候是表示成功申请WSAStartup
}
等同于:if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){// 返回值为零的时候是表示成功申请WSAStartup
Printf(""加载套接字失败:%d......\n",WSAGetLastError());
}
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { //检测版本号
// 检查这个低字节是不是2,高字节是不是1以确定是否我们所请求的2.2版本
// 否则的话,调用WSACleanup()清除信息,结束函数
WSACleanup( );
return -1;
}
void initialization() {
//初始化套接字库
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}
(3)填充地址信息: SOCKADDR_IN server_addr,client_addr;
网络通信地址:sockaddr_in结构体
struct sockaddr { //sockaddr是给操作系统使用
unsigned short sa_family; // 2 bytes address family, AF_xxx,sa_family是地址家族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET,代表TCP/IP协议族
char sa_data[14]; // 14 bytes of protocol address
};
typedef struct sockaddr_in { //程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口
short sin_family; // 2 bytes e.g. AF_INET, AF_INET6
//sockaddr用其余14个字节来表示sa_data, sockaddr_in把14个字节拆分成sin_port, sin_addr和sin_zero
unsigned short sin_port; // 2 bytes e.g. htons(3490), 端口号
IN_ADDR sin_addr; //struct in_addr sin_addr; // 4 bytes see struct in_addr, below, IP 地址
char sin_zero[8]; // 8 bytes zero this if you want to, sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小
}SOCKADDR_IN;
typedef 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;
#define s_addr S_un.S_addr
} IN_ADDR;
例:SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET;
server_addrl.sin_port = htons(10091);
server_addr.sin_addr.s_addr = inet_addr(INADDR_ANY); 等价于 server_addr.sin_addr.S_un.S_addr = inet_addr(INADDR_ANY);
(4)绑定/监听/接受 bind/listen/accept
if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "套接字绑定失败!" << endl;
WSACleanup();
}
else {
cout << "套接字绑定成功!" << endl;
}
//设置套接字为监听状态
if (listen(s_server, SOMAXCONN) < 0) {
cout << "设置监听状态失败!" << endl;
WSACleanup();
}
else {
cout << "设置监听状态成功!" << endl;
}
cout << "服务端正在监听连接,请稍候...." << endl;
//接受连接请求
len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
if (s_accept == SOCKET_ERROR) {
cout << "连接失败!" << endl;
WSACleanup();
return 0;
}
cout << "连接建立,准备接受数据" << endl;
(5)接收/发送数据 send/recv
while (1) {
recv_len = recv(s_accept, recv_buf, 100, 0);
if (recv_len < 0) {
cout << "接受失败!" << endl;
break;
}
else {
cout << "客户端信息:" << recv_buf << endl;
}
cout << "请输入回复信息:";
cin >> send_buf;
send_len = send(s_accept, send_buf, 100, 0);
if (send_len < 0) {
cout << "发送失败!" << endl;
break;
}
}
(6)关闭套接字
closesocket(s_server);//关闭套接字
closesocket(s_accept);
WSACleanup();//释放DLL资源
(7)客户端连接
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "服务器连接失败!" << endl;
WSACleanup();
}
else {
cout << "服务器连接成功!" << endl;
}
示例:server 端
#include "pch.h"
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization();
int main() {
//定义长度变量
int send_len = 0;
int recv_len = 0;
int len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[100];
char recv_buf[100];
//定义服务端套接字,接受请求套接字
SOCKET s_server;
SOCKET s_accept;
//服务端地址客户端地址
SOCKADDR_IN server_addr;
SOCKADDR_IN accept_addr;
initialization();
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(5010);
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "套接字绑定失败!" << endl;
WSACleanup();
}
else {
cout << "套接字绑定成功!" << endl;
}
//设置套接字为监听状态
if (listen(s_server, SOMAXCONN) < 0) {
cout << "设置监听状态失败!" << endl;
WSACleanup();
}
else {
cout << "设置监听状态成功!" << endl;
}
cout << "服务端正在监听连接,请稍候...." << endl;
//接受连接请求
len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
if (s_accept == SOCKET_ERROR) {
cout << "连接失败!" << endl;
WSACleanup();
return 0;
}
cout << "连接建立,准备接受数据" << endl;
//接收数据
while (1) {
recv_len = recv(s_accept, recv_buf, 100, 0);
if (recv_len < 0) {
cout << "接受失败!" << endl;
break;
}
else {
cout << "客户端信息:" << recv_buf << endl;
}
cout << "请输入回复信息:";
cin >> send_buf;
send_len = send(s_accept, send_buf, 100, 0);
if (send_len < 0) {
cout << "发送失败!" << endl;
break;
}
}
//关闭套接字
closesocket(s_server);
closesocket(s_accept);
//释放DLL资源
WSACleanup();
return 0;
}
void initialization() {
//初始化套接字库
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}
client 端
#include "pch.h"
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization();
int main() {
//定义长度变量
int send_len = 0;
int recv_len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[100];
char recv_buf[100];
//定义服务端套接字,接受请求套接字
SOCKET s_server;
//服务端地址客户端地址
SOCKADDR_IN server_addr;
initialization();
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(1234);
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "服务器连接失败!" << endl;
WSACleanup();
}
else {
cout << "服务器连接成功!" << endl;
}
//发送,接收数据
while (1) {
cout << "请输入发送信息:";
cin >> send_buf;
send_len = send(s_server, send_buf, 100, 0);
if (send_len < 0) {
cout << "发送失败!" << endl;
break;
}
recv_len = recv(s_server, recv_buf, 100, 0);
if (recv_len < 0) {
cout << "接受失败!" << endl;
break;
}
else {
cout << "服务端信息:" << recv_buf << endl;
}
}
//关闭套接字
closesocket(s_server);
//释放DLL资源
WSACleanup();
return 0;
}
void initialization() {
//初始化套接字库
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}
参考:
Windows下与Linux下编写socket程序的区别
http://blog.chinaunix.net/uid-2270658-id-308160.html
sockaddr_in , sockaddr , in_addr区别Socket编程函数集(非常有用)
http://810364804.iteye.com/blog/2094531
ntohs, ntohl, htons,htonl的比较和详解
http://www.360doc.com/content/12/0222/10/54470_188560773.shtml
socket编程 sockaddr sockaddr_in