网络编程基础
-
进程的网络地址:
进程是出于运行过程中的程序实例,是操作系统调度和分配的基本单位。每个进程至少拥有一个线程否则,系统会自动撤销该进程和它的地址空间。
-
三种套接字:
- 数据报套接字:无连接,不保证可靠,独立的数据传输服务。
- 流式套接字:提供双向、有序、无重复、无边界记录、可靠的数据传输服务。需要事先建立连接。
> 面向连接的协议和面向无连接的区别在明白了这个之后变得很清楚。
- 原始套接字:允许直接访问较低层的协议(如IP,ICMP)用与检验新的协议的实现。
-
网络协议的特征:
- 面向消息的协议与基于流的协议:
- 面向消息的协议:以消息为单位在网上传输数据。一条一条的发送与接收。每条消息间都是独立的,存在着消息边界。
- 基于流的协议: 不保护消息的边界,将数据拆封成字节流连续的传输,不管实际消息边界是否存在,TCP就是基于流的协议。
- 面向连接和无连接的服务:
- 面向连接:每一次完整的传输数据都要建立连接。在数据传输中,各数据分组不携带目的地址,而是使用连接号。本质上讲,连接是一个管道。通信前通过握手,相互传送连接信息,一方面确定了通行的路径,另一方面还可以相互协商,做好通信的准备,例如:准备收发的缓冲区。
- 面向无连接:类似邮政系统服务的抽象。每个分组都携带完整的目的地址,各分组在系统中独立传送。无法保证分组先后到达的顺序,不进行分组的恢复与重传,不保证传输的可靠性。UDP就是无连接的协议。
- 可靠性和次序性:
可靠性:可靠性保证了发送端发送的每一个字节都达到既定的接受端,保证数据的完整性。
次序性:保证收到的数据的顺序就是发送端发送的顺序。
-
P2P(对等网、点对点)
- P2P技术:在计算机直接直接进行资源和服务共享。
- 特征:
- 分散性:分布式系统
- 规模性:
- 扩展性:随时加入
- Servent性(sever&client)
- 自治性:自行加入与退出
- 互助性
- 自组织性:自行组织不需要任何管理员
-
Internet中网间进程的标识
- 用一个三元组可以在全局中唯一的标识一个应用层进程:
- 传输层协议
- 主机的IP地址
- 传输层的端口号
- 用一个三元组可以在全局中唯一的标识一个应用层进程:
Winsock下文件传输:
文件传输
1, 注册套接字函数socket(int af,int type,int protocol)
参数解释:
af : 一个地址描述 ,目前只支持AF_INET格式,也就是说在本次实验中此参数直接写
为AF_INET即可。
type: 指定socket类型,例如 TCP类型(SOCK_STREAM) UDP类型(SOCK_DGRAM)
protocol: 套接口制订协议类型 可以为0,。 常用类型有 IPPROTO_TCP 、IPPROTO_UDP等
2, 绑定套接字函数 bind ( SOCKET s , const struct sockaddr FAR *addr , int namelen );
参数解释:
s :socket()函数返回的套接口描述字
addr : 指向Socket地址的指针
namelen :该地址的长度
3,监听函数listen(SOCKET s,int backlog)
参数解释:
s: socket()函数返回的套接口描述字
backlog:等待连接队列的最大长度
4,接收连接函数accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen)
参数解释:
s: socket()函数返回的套接口描述字
addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。addr参数的实际格式由套接口创建时所产生的地址族确定
addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数
5, 发送连接请求函数connect(SOCKET s,const struct coskaddr FAR* name,int namelen)
参数解释:
s: socket()函数返回的套接口描述字
name: 想要进行连接的端口名
namelen: 名字长度
6,发送数据函数send( SOCKET s, const char FAR* buf, int len, int flags)
接收文件函数recv(SOCKET s, char FAR* buf, int len, int flags)
参数解释:
s:socket()函数返回的套接口描述字
buf:用于接收数据的缓冲区
len:缓冲区长度
flags:指定调用方式
7,文件操作函数fopen(const char * path,const char * mode) fclose(FILE* stream)
参数解释:
path:欲打开的文件路径及文件名
mode:代表流形态
stream:文件流指针
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#define MAXLINE 1024
#define SEVERPORT 1026
#define SEVERPORTADDR "192.168.1.110"
int main ()
{
WSADATA wsaData;//wsdate 用于存储调用windock中socket初始化函数返回的初始化信息
WORD socketVersion = MAKEWORD(2.2); //就是一个word地位用来指明主版本号,高位用来指明修正号
//初始化windows socket
if (0 != WSAStratup(socketVersion,&wsaData)) {
printf("初始化失败!\n");
exit(0);
}
char recvbuff[MAXLINE + 1]; //创建缓冲区
int sockfd,n;//socket标志字,那么fd是什么意思呢,fd是file descriptor的缩写。顾名思义就是文件描述符啦。
//设置地址
struct sockaddr_in servaddr;
//给地址初始化
memset(&servaddr,0,sizeof(servaddr));
//设置使用IPV4通信
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SEVERPORT);//host to network short int;
//sin_addr中的联合S_un包含的s_un.s_addr 意思为An IPv4 address formatted as a u_long
servaddr.sin_addr.S_un.S_addr = inet_addr(SEVERPORTADDR);
//生成套接字
if ((sockfd = socket(AF_INET,SOCK_STREAM.0)) < 0) {
printf("sockfd error!\n");
exit(0);
}
//尝试连接服务器
printf("请输入要传输的文件\n");
char filename[256];
scanf("%s",filename);
//建立文件缓冲区,定义文件指针,指向传输文件
FILE * fp = (FILE *)NULL;//初始化文件指针
fp = fopen(filename,"rb"); //用二进制格式打开一个文件,只允许读。
if (fp == (FILE *)NULL) {
printf("打开文件失败!\n");
return 0;
}
//发送文件名
n = send(sockfd, filename, strlen(filename), 0);
//如果无错误,返回值为发送数据的总数
printf("发送数据为:%d\n",n);
//接受服务器返回的信息
n = recv(sockfd,recvbuff,MAXLINE,0);
if(strcmp(recvbuff, "ok") == 0){//如果服务器返回ok则开始传送文件
memset(recvbuff, 0, sizeof(recvbuff));
while(!feof(fp)){
n = fread(recvbuff, sizeof(char), 1000, fp);
printf("%d\n", n);
n = send(sockfd, recvbuff, n, 0);
printf("send n = %d\n", n);
}//传送文件
fclose(fp);//关闭文件
}
closesocket(sockfd);
//关闭网络描述符
return 0;
}
服务器
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<WinSock2.h>
#define PORTNUMBER 3333 //定义端口号
#define MAXLINE 1024 //缓存大小
#pragma comment(lib,"ws2_32.lib")
#define QueueLen 1
char buffer[MAXLINE+1];
int n;
int main(int argc, char * argv[])
{
WORD version;
version = MAKEWORD(1, 1);
WSADATA wsa;
if(0 != WSAStartup(version, &wsa)){
printf("WSAStartup error!\n");
exit(0);
}//初始化 socket
struct sockaddr_in servaddr; //服务器IP地址
struct sockaddr_in clientaddr;//客户端IP地址
int listenfd;//监听的网络描述符
int clientfd;//客户端网络描述符
int alen;
memset(&servaddr, 0, sizeof(servaddr)); //初始化
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY 你网卡上的任意IP,比如你有多个IP
servaddr.sin_port = htons((unsigned short) PORTNUMBER);
//初始化服务器
listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP );
//初始化监听的网络描述符
if(listenfd< 0){
fprintf(stderr, "socket createion failed!\n");
exit(0);
}//失败
if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){
fprintf(stderr, "bind failed!\n");
exit(0);
}//版定到端口
if(listen(listenfd, QueueLen) < 0){
fprintf(stderr, "listen failed!\n");
exit(0);
}//绑定失败
char filename[256];
while(1){
alen = sizeof(clientaddr);
if((clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &alen)) < 0){
fprintf(stderr, "accept failed!\n");
exit(0);
}//接受客户端的连接
n = recv(clientfd, filename, 255, 0);//接受客户端发送的信息
filename[n] = '\0';
printf("recv n = %d\n", n);
FILE * fp = (FILE *)NULL;
if((FILE *) NULL == (fp = fopen(filename, "wb"))){
printf("open file %s failed!\n", filename);
return 0;
}
n = send(clientfd, "ok", strlen("ok"), 0);
//发送 ok 到客户端,让客户端开始发送文件
printf("send n = %d\n", n);
while(n = recv(clientfd, buffer, 1000, 0)){
printf("recv n = %d\n", n);
fwrite(buffer, sizeof(char), n, fp);
if(n != 1000)
break;
}//接受文件并保存
fclose(fp);
closesocket(clientfd);
//关闭文件,关闭网络描述符
}
return 0;
}