一、网络编程的基本概念
1、socket概念
英文是插座的意思,是网络数据传输用的软件设备,创建一个socket相当于安装了一部电话机。
2、C/S概念
- 客户机/服务器模式在操作过程中采用主动请求的方式。
- 服务器端:
- 首先服务器先启动,并根据请求提供相应的服务
- 打开一个通信通道,在某一地址和端口上接受请求。
- 等待客户请求到达端口
- 接受重复服务请求,处理该请求并发送应答信号
- 返回第二步,等待另一客户请求
- 关闭服务器
- 客户端
- 打开一个通信通道,并连接到服务器所在主机的特定端口
- 向服务器发服务请求,等待并接收应答;继续提出请求。
- 请求结束后关闭通道并终止。
3、面向连接与面向消息(TCP、UDP)
面向连接:
相当于A与B之间有了一个传送带(网络)
特点:
- 传输过程中数据不会丢失(可靠)
- 按顺序传输数据
- 不存在数据边界
面向消息:
特点:
- 传输的数据可能丢失可能销毁
- 强调快速传输而非顺序
- 限制传输数据大小
- 传输的数据有数据边界
二、网络编程
1、套接字类型与协议设置
- SOCK_STREAM[流套接字]
面向连接可靠的传输数据 是和传输大量的数据,不支持广播多播
- SOCK_DGRAM[数据报套接字]
无连接 广播 多播
- SOCK_RAW[原始套接字]
避开TCP/IP处理机制,可以直接传送给需要他的应用
引用头文件
#include<winsock2.h>
导入ws2_32.lib库
windows下的socket编程都要首先进行Winsock的初始化
2、网络编程基本函数和基本数据结构
函数名称 | 功能描述 | 适用范围 |
socket | 创建套接字 | 面向连接+面向无连接的传输 |
bind | 套接字与本地IP地址和端口号的绑定 | 面向连接+面向无连接的传输 |
connect | 请求连接 | 面向连接的传输的客户机进程 |
listen | 侦听连接请求 | 面向连接的传输的服务器进程 |
accept | 接受连接请求 | 面向连接的传输的服务器进程 |
send | 往已建立连接的套接字上发送数据 | 面向连接的传输 |
recv | 往已建立连接的套接字上接收数据 | 面向连接的传输 |
sendto | 在无连接的套接字上发送数据 | 主要用以无连接的传输 |
recvfrom | 在无连接的套接字上接收数据 | 主要用以无连接的传输 |
close | 关闭套接字 | 面向连接+面向无连接的传输 |
数据结构:
操作系统用
typedef struct sockaddr {
#if (_WIN32_WINNT < 0x0600)
u_short sa_family;
#else
ADDRESS_FAMILY sa_family; // Address family. 16位地址类型
#endif //(_WIN32_WINNT < 0x0600)
CHAR sa_data[14]; // Up to 14 bytes of direct address.
}
程序员用
typedef struct sockaddr_in {
#if(_WIN32_WINNT < 0x0600)
short sin_family; //16位地址类型
#else //(_WIN32_WINNT < 0x0600)
ADDRESS_FAMILY sin_family;
#endif //(_WIN32_WINNT < 0x0600)
USHORT sin_port; //16位端口号
IN_ADDR sin_addr; //32位IP
CHAR sin_zero[8]; //8字节填充
}
三、手写简单服务器
TCP流程:
服务器端代码:
#include<winsock2.h>
#include<stdio.h>
#include<stdlib.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
printf("TCP Sever\n");
//初始化网络库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2,2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup errorNum = %d\n",GetLastError());
return err;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("LOBYTE errorNum = %d\n",GetLastError());
WSACleanup();
return -1;
}
//1 安装电话机 选中函数按F1
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0); //第三个参数由前两个决定 默认0就行
//2 分配电话号码
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
//
if (SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
printf("bind errorno = %d\n", GetLastError());
return -1;
}
//3 监听
if (SOCKET_ERROR == listen(sockSrv, 5))
{
printf("listen errorno = %d\n", GetLastError());
return -1;
}
printf("开始延迟");
Sleep(20000);
printf("结束延迟");
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
while (true)
{
//4 分配一台分ji处理客户端连接 拿起电话机准备接听
printf("begin accept\n");
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli,&len);
printf("end accept\n");
char sendBuf[100] = { 0 };
sprintf_s(sendBuf, 100, "Welcome to China!");
//5 开始童话 fa收数据
int iLen = send(sockConn,sendBuf,strlen(sendBuf),0);
char recvBuf[100] = { 0 };
iLen = recv(sockConn, recvBuf, 100, 0);
printf("recvBuf = %s\n", recvBuf);
//6 关闭分机
closesocket(sockConn);
}
// 关闭总机
closesocket(sockSrv);
system("pause");
return 0;
}
客户端代码:
#include<winsock2.h>
#include<stdio.h>
#include<stdlib.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
printf("TCP Client\n");
//初始化网络库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("LOBYTE errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
//1 安装电话机 选中函数按F1
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0); //第三个参数由前两个决定 默认0就行
if (INVALID_SOCKET == sockCli)
{
printf("socket errorno = %d\n", GetLastError());
return -1;
}
//2 配置要链接的服务器
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.7");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
//3 连接服务器
if (SOCKET_ERROR == connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
printf("bind errorno = %d\n", GetLastError());
system("pause");
return -1;
}
//4 收发数据
char recvBuf[100] = { 0 };
int iLen = recv(sockCli, recvBuf, 100, 0);
printf("recvBuf = %s\n", recvBuf);
const char sendBuf[100] = "zsywan";
iLen = send(sockCli, (char*)sendBuf, 100, 0);
//5 关闭总ji
closesocket(sockCli);
WSACleanup();
system("pause");
return 0;
}