在学习网络编程之前,我们需要对Tcp/ip协议栈有个清楚地了解。Tcp/ip协议栈分以下五层,也有分成7层
5、应用层
4、传输层
3、网络层
2、数据链路层
1、物理层
物理层是实实在在的物理链路,它为上层协议提供了一个传输数据的物理媒体,负责将数据以比特流的方式收发、接收。数据链路层主要负责物理传输的准备,MAC地址和交换机都工作在这一层。网络层主要负责管理网络地址、定位设备、决定路由。路由器就工作在这一层。作用是将网路地址翻译成物理地址,并决定将数据从发送方路由到接收方。应用层,一般是指应用程序,该层主要负责确定通信对象,并确保有足够资源用于通信。根据传输层的端口信息调用不同的程序来处理传输的内容,端口8080是http报文,端口21是ftp报文。
物理层关心的是如何把电气信号变成一段报文;数据链路层关心的是mac地址、vlan、优先级等;网络层关心的是ip地址,下一跳ip;传输层关心的是端口资源;应用层关心的是报文组装、解析、渲染、存储、执行等等。
在学习网络编程之前,学习socket的工作原理是很有必要的。我们需要为它配置端口信息和ip地址。配置完了之后,我们就可以慢慢等待报文的收发了。所以一般来说,作为服务器端口的处理流程是这样的:
a) 创建socket
b) 绑定socket到特定的ip地址
c) 对socket进行侦听处理
d) 接受socket,表明有客户端和服务器连接
e) 和客户端循环收发报文
f) 关闭socket
客户端的处理流程就相对简单一点了,只需要
a) 创建socket
b) 链接服务器端地址
c) 和服务器端的socket收发报文
一、Winsocket用TCP协议来完成通信
①服务器的程序
// SocketSever.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"WS2_32.LIB")
#if DUBUG
#define AF_INET 2 /* internetwork: UDP, TCP, etc. */
#define SOCK_STREAM 1 /* stream socket */
#define SOCK_DGRAM 2 /* datagram socket */
#define SOCK_RAW 3 /* raw-protocol interface */
#define SOCK_RDM 4 /* reliably-delivered message */
#define SOCK_SEQPACKET 5 /* sequenced packet stream */
/*
* Socket address, internet style.
*/
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
typedef struct sockaddr_in SOCKADDR_IN; /* Microsoft Windows Extended data types */
int bind(
SOCKET s,
const struct sockaddr FAR * name,
int namelen
); /* INCL_WINSOCK_API_PROTOTYPES*/
int listen(
SOCKET s,
int backlog
);
#endif
#define Port 3578
#define MaxSize 1024
SOCKADDR_IN addrSrv;
SOCKADDR_IN addrClient;
SOCKET sockSrv; //服务器
SOCKET sockConn;
//********************************************************************************************************/
//** 函数名 ** StartSock()
//** 输入 ** 无
//** 输出 ** 无
//**函数描述** 加载套接字
//********************************************************************************************************/
int SocketInit()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested=MAKEWORD(2,2);
err = WSAStartup(wVersionRequested,&wsaData); //返回0,成功,否则就是错误码
if (err!=0)
{
printf("WinSock DLL版本不足要求n");
return 0;
}
if (LOBYTE(wsaData.wVersion)!=2||
HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
return 0;
}
return 1;
}
//********************************************************************************************************/
//** 函数名 ** main()
//** 输入 ** 无
//** 输出 ** 无
//**函数描述** 主函数
//********************************************************************************************************/
int main()
{
if (SOCKET_ERROR ==SocketInit())
{
return -1;
}
SOCKADDR_IN addrSrv;//存放本地地址信息的
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//htol将主机字节序long型转换为网络字节序
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6001);//htos用来将端口转换成字符,1024以上的数字即可
//真正socket编程部分
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//面向连接的可靠性服务SOCK_STRAM
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//将socket绑定到相应地址和端口上
listen(sockSrv,5);//等待队列中的最大长度为5
printf("%s\n","Welcome,the Host is running!Now Wating for someone comes in!");
SOCKADDR_IN addrClient;
int iSend = 0;
char sendBuf[MaxSize];
char revBuf[MaxSize];
int len=sizeof(SOCKADDR);
while(1) //循环监听客户端,永远不停止
{
sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//阻塞调用进程直至新的连接出现
if(sockConn == INVALID_SOCKET)
{
printf("Accept Failed!\n");
return -1;
}
sprintf(sendBuf,"Server IP is:%s",inet_ntoa(addrClient.sin_addr));//inet_nota函数是将字符转换成ip地址
iSend = send(sockConn,sendBuf,strlen(sendBuf)+1,0);//服务器向客户端发送数据
if(iSend == SOCKET_ERROR)
{
printf("send Failed!");
break;
}
char recvBuf[100];
recv(sockConn,recvBuf,100,0);//服务器从客户端接受数据
printf("%s\n",recvBuf);
closesocket(sockConn);//关闭socket
}
closesocket(sockConn); //关闭套接字
getchar();
return 0;
}
②客户端的程序
<pre name="code" class="cpp">#include "stdafx.h"
#include <Winsock2.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib,"WS2_32.LIB")
//********************************************************************************************************/
//** 函数名 ** StartSock()
//** 输入 ** 无
//** 输出 ** 无
//**函数描述** 加载套接字
//********************************************************************************************************/
int SocketInit()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested=MAKEWORD(2,2);
err = WSAStartup(wVersionRequested,&wsaData); //返回0,成功,否则就是错误码
if (err!=0)
{
printf("WinSock DLL版本不足要求n");
return 0;
}
if (LOBYTE(wsaData.wVersion)!=2||
HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
return 0;
}
return 1;
}
//********************************************************************************************************/
//** 函数名 ** main()
//** 输入 ** 无
//** 输出 ** 无
//**函数描述** 主函数
//********************************************************************************************************/
int main()
{
//固定格式
char recvBuf[1024];
if (SOCKET_ERROR ==SocketInit())
{
return -1;
}
//建立通讯socket
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("192.168.1.100");//设定需要连接的服务器的ip地址
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6001);//设定需要连接的服务器的端口地址
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//与服务器进行连接
//接受来自老师的信息
int iRevLen = 0;
iRevLen = recv(sockClient,recvBuf,strlen(recvBuf)+1,0);
if(0 == iRevLen)
{
return -1;
}
else if(SOCKET_ERROR == iRevLen)
{
printf("Recv Failed\n!");
return -1;
}
printf("%s\n",recvBuf);
char str[100];
gets(str);
send(sockClient,str,strlen(str)+1,0);
closesocket(sockClient);
WSACleanup();
getchar();
return -1;
}