网络编程篇
- 一、网络编程概述
- 二、字节序
- 三、socket编程步骤
- 四、socket服务端和客户端代码的初步实现
- 五、简单的ftp项目实现的功能有哪些?
- 六、ftp项目服务器和客户端代码实现
一、网络编程概述
1.进程间通信:
1)进程间通信的方式有**:管道,消息队列,共享内存,信号,信号量这么集中
2)特点:依赖于linux内核,基本是通过内核来实现应用层的两个进程间的通信
3)缺陷:无法多机通讯
2.网络编程:
1)网络编程适用去不同的pc间的通信,可以实现多机运行
2)它关心的是:地址和数据
地址是指:IP地址和端口号。每台PC机连网够都有一个IP地址,那么每台联网的PC机可能跑多个服务器,然后每个服务器中对应的有很多进程。当客户端接入的时候,不知道去对接哪个服务器。那么这时端口号对应的就是每个服务器的端口。客户就可以通过端口号连接到对应的服务器了。
数据是指:协议(HTTP/TCP/UDP)
它是一种数据格式
3.TCP/UDP对比
1)TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2)TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大的努力交付,即不保证可靠交付
3)TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没用拥塞控制,因此网络出现拥塞不会使源主机的发送速率减低(对实时应用很有用,如IP电话,实时视频会议等)
4)每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5)TCP首部开销字节;UDP的首部开销小,只有8字节
6)TCP的逻辑通信信道是全双工的可靠信道;UDP则是不可靠信道
4.端口号的作用
一台拥有IP地址的主机可以提供许多服务,比如Web服务,FTP服务,SMTP服务
这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务的呢?显然不能只靠IP地址,因为IP地址和网络服务的关系是一对多的关系。
实际上是通过“IP地址+端接口”来区分不同服务的。
端口提供了一种访问通道,
服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69
二、字节序
1.定义
字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
2.常见序
1)Little endian(小端字节序):将低序字节存储在起始地址
LE little-endian
最符合人的思维的字节序
地址低位存储值的低位
地址高位存储值的高位
怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说
低位值小,就应该放在内存地址小的地方,也即内存地址低位
反之,高位值就应该放在内存地址大的地方,也即内存地址高位
2) Big endian(大端字节序):将高序字节存储在起始地址
BE big-endian
最直观的字节序
地址低位存储值的高位
地址高位存储值的低位
为什么说直观,不要考虑对应关系
只需要把内存地址从左到右按照由低到高的顺序写出
把值按照通常的高位到低位的顺序写出
两者对照,一个字节一个字节的填充进去
三、socket编程步骤
说来说去,还是调用linux内核函数,人家都已经给你做好了,要怎么使用该函数,直接调用系统API就行。
1.创建套接字
函数原型:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:
指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)
AF_INET IPv4 因特网域
AF_INET6 IPv6 因特网域
AF_UNIX Uinx 域
AF_ROUTE 路由套接字
AF_KEY 密匙套接字
AF_UNSPEC 未指定
type参数指定socket的类型:
SOCK_STREAM:
流式套接字提供可靠的,面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性
SOCK_DGRAM:
数据报套接字定义了一种无连接的服,数据通过相互独立的报文传输,是无序了,并且不保证是可靠,无差错的。它使用数据包协议UDP
SOCK_RAW:
允许程序使用底层协议,原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发
protocol:
通常赋值:0
0选择type类型对应的默认协议
IPPROTO_TCP TCP传输协议
IPPROTO_UDP UDP传输协议
IPPROTO_SCTP SCTPC传输协议
IPPROTO_TIPC TIPC传输协议
2.bind()函数:IP端口号与相应描述字赋值函数
函数原型:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:
用于绑定IP地址和端口号到socketfd
参数:
sockfd
是一个socket描述符
addr
是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同
ipv4对应的是:
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
同等替换:
struct sockaddr_in{
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
}
地址转换API
int inet_aton(const char* straddr,struct in_addr *addrp);
char* inet_ntoa(struct in_addr sin_addr);
3.监听
listen()函数:监听设置函数
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:
设置能处理的最大连接数,listen() 并未开始接收连接,只是设置socket 的 listen 模式,listen 函数只用于服务端,服务器进程不知道要与谁连接,因此,它不会主动要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对他做出处理,一个服务进程可以同时处理多个客户进程的连接。主要就两个功能:将一个未连接的套接字转换为一个被动的套接字(监听),规定内核为相应套接字排队的最大连接数。
内核为任何一个给定监听套接字维护两个队列:
未完成连接队列,每个这样的SYN 报文段对应其中一项:已由某个客户端发出并到达服务器,而服务器正在等待完成相应的TCP 三次握手过程。这些套接字处于SYN_REVD 状态;
已完成连接队列,每个已完成TCP 三次握手过程的客户端对应其中一项。这些套接字处于ESTABLISHED 状态;
参数
sockfd
sockfd 是socket 系统调用返回的服务器端socket 描述符
backlog
backlog 指定在请求队列中允许的最大请求数
4.接受连接
accept()函数
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:
accept 函数由TCP 服务器调用,用于从已完成连接队列对头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。
参数:
sockfd
sockfd 是socket 系统调用返回的服务器端socket 描述符
addr
用来返回已连接的对端(客户端)的协议地址
addrled
客户端地址长度
返回值:
该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符,而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接收的客户连接创建一个已连接套接字(表示TCP 三次握手已完成),当服务器完成对某个给定客户的服务器时,相应的已连接套接字就会被关闭。
5.数据的收发:
字节流读取函数
在套接字通信中进行字节读取函数:read(); write(); 与I/O 中的读取读取函数略有区别,因为它们输入或输出的字节数比可能比请求的要少。
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
6.客户端的connect函数:
connect()函数:客户连接主机
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:
该函数用于绑定之后的client 端(客户端),与服务器建立连接
参数:
sockfd:是目的服务器的socket 描述符
addr:是服务器端的IP 地址和端口号的地址结构指针
addrlen:地址长度,常被设置为sizeof(struct sockaddr)
返回值:
成功返回0,遇到错误时返回 -1,并且error中包含相应的错误码
四、socket服务端和客户端代码的初步实现
1.服务端代码:
#include <sys/types.h>
#include <sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include <arpa/inet.h>
#include<string.h>
#include <unistd.h>
int main()
{
int s_fd;
int c_fd;
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1){
perror("socket");
}
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
char readBuf[128] = {0};
char *str = "i am handsome";
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8988);
inet_aton("192.168.4.102",&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
listen(s_fd,10);
int clen = sizeof(struct sockaddr_in);
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd == -1){
perror("accept");
}
printf("有数据连接,它的IP地址是:%s\n",inet_ntoa(c_addr.sin_addr));
int nread = read(c_fd,&readBuf,128);
if(nread == -1){
perror("read");
}else{
printf("读取到数据:%s\n",readBuf);
}
int nwrite = write(c_fd,str,strlen(str));
return 0;
}
2.客户端代码:
#include <sys/types.h>
#include <sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include <arpa/inet.h>
#include<string.h>
#include <unistd.h>
int main()
{
int c_fd;
char *str = "i am handsome";
char readBuf[129] = {0};
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1){
perror("socket");
exit(-1);
}
struct sockaddr_in c_addr;
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(8989);
inet_aton("192.168.4.102",&c_addr.sin_addr);
if(connect(c_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr)) == -1){
printf("连接服务器失败!\n");
exit(-1);
}
write(c_fd,str,strlen(str));
read(c_fd,readBuf,129);
printf("读取到的数据是:%s\n",readBuf);
return 0;
}
运行结果:
五、简单的ftp项目实现的功能有哪些?
客户端:
1)获取服务器的文件 get xxx
2)展示服务器有哪些文件 ls
3)进入服务器的某文件夹 cd xxx
4)上传文件到服务器 put xxx
5)查看客户端本地文件 lls
6)进入客户端某文件夹 lcd xxx
服务端:
1)解析客户端指令并执行
2)把执行结果反馈给客户端
六、ftp项目服务器和客户端代码实现
1.服务器代码:
#include <sys/types.h>
#include <sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include <arpa/inet.h>
#include<string.h>
#include <unistd.h>
#include"config.h"
#include <fcntl.h>
int get_cmd_type(char *cmd)
{
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("pwd",cmd)) return PWD;
if(strstr(cmd,"cd")!=NULL) return CD;
if(strstr(cmd,"get")!=NULL) return GET;
if(strstr(cmd,"put")!=NULL) return PUT;
return 100;
}
char *getDesDir(char *cmsg)
{
char *p;
p = strtok(cmsg," ");
p = strtok(NULL," ");
return p;
}
void msg_handler(struct Msg msg,int fd)
{
char cmdBuf[1024] = {0};
char *file = NULL;
int fdfile;
printf("客户端输入的指令是:%s\n",msg.cmd);
int ret = get_cmd_type(msg.cmd);
switch(ret){
case LS:
case PWD:
msg.type = 0;
FILE *r = popen(msg.cmd,"r");
fread(msg.cmd,sizeof(msg.cmd),1,r);
write(fd,&msg,sizeof(msg));
break;
case CD:
msg.type = 1;
char *dir = getDesDir(msg.cmd);
printf("切割后返回的是:%s\n",dir);
chdir(dir);
break;
case GET:
file = getDesDir(msg.cmd);
if(access(file,F_OK) == -1){
strcpy(msg.cmd,"文件不存在!");
write(fd,&msg,sizeof(msg));
}else{
msg.type = DOFILE;
fdfile = open(file,O_RDWR);
read(fdfile,cmdBuf,sizeof(cmdBuf));
close(fdfile);
strcpy(msg.cmd,cmdBuf);
write(fd,&msg,sizeof(msg));
}
break;
case PUT:
fdfile = open(getDesDir(msg.cmd),O_RDWR|O_CREAT,0600);
write(fdfile,msg.secondBuf,strlen(msg.secondBuf));
close(fdfile);
break;
case QUIT:
printf("客户断开连接!\n");
exit(-1);
}
}
int main(int argc,char **argv)
{
if(argc != 3){
perror("param");
exit(-1);
}
int s_fd;
int c_fd;
int n_read;
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
struct Msg msg;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
listen(s_fd,10);
int clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd == -1){
perror("accpet");
}
printf("数据接入成功,它的IP是:%s\n",inet_ntoa(c_addr.sin_addr));
if(fork() == 0){
while(1){
memset(msg.cmd,0,sizeof(msg.cmd));
n_read = read(c_fd,&msg,sizeof(msg));
if(n_read == 0){
printf("客户断开连接!\n");
break;
}else if(n_read > 0 ){
msg_handler(msg,c_fd);
}
}
}
}
close(s_fd);
close(c_fd);
return 0;
}
congfig头文件:
#define LS 0
#define GET 1
#define PWD 2
#define IFGO 3
#define LCD 4
#define LLS 5
#define CD 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
struct Msg
{
int type;
char cmd[1024];
char secondBuf[128];
};
2.客户端代码:
#include <sys/types.h>
#include <sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include <arpa/inet.h>
#include<string.h>
#include <unistd.h>
#include"config.h"
#include <fcntl.h>
char *getdir(char *cmd)
{
char *p;
p = strtok(cmd, " ");
p = strtok(NULL," ");
return p;
}
int get_cmd_type(char *cmd)
{
if(strstr(cmd,"lcd")) return LCD;
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("lls",cmd)) return LLS;
if(!strcmp("pwd",cmd)) return LS;
if(strstr(cmd,"cd")) return CD;
if(strstr(cmd,"get")) return GET;
if(strstr(cmd,"put")) return PUT;
return -1;
}
int cmd_handler(struct Msg msg,int fd)
{
char *dir = NULL;
char buf[32];
int ret;
int filefd;
ret = get_cmd_type(msg.cmd);
switch(ret){
case LS:
case CD:
case PWD:
msg.type = 0;
write(fd,&msg,sizeof(msg));
break;
case GET:
msg.type = 2;
write(fd,&msg,sizeof(msg));
break;
case PUT:
strcpy(buf,msg.cmd);
dir = getdir(buf);
if(access(dir,F_OK) == -1){
printf("%s not exsit\n",dir);
}else{
filefd = open(dir,O_RDWR);
read(filefd,&msg.secondBuf,sizeof(msg.secondBuf));
close(filefd);
write(fd,&msg,sizeof(msg));
}
break;
case LLS:
system("ls");
break;
case LCD:
dir = getdir(msg.cmd);
chdir(dir);
break;
case QUIT:
strcpy(msg.cmd,"quit");
write(fd,&msg,sizeof(msg));
close(fd);
exit(-1);
}
return ret;
}
void handler_server_message(int c_fd,struct Msg msg)
{
int n_read;
struct Msg msgget;
int newfilefd;
n_read = read(c_fd,&msgget,sizeof(msgget));
if(n_read == 0){
printf("server is out,qiut\n");
exit(-1);
}
else if(msgget.type == DOFILE){
char *p = getdir(msg.cmd);
newfilefd = open(p,O_RDWR|O_CREAT,0600);
write(newfilefd,msgget.cmd,strlen(msgget.cmd));
putchar('>');
fflush(stdout);
}
else{
printf("-----------------------");
printf("\n%s\n",msgget.cmd);
printf("-----------------------");
putchar('>');
fflush(stdout);
}
}
int main(int argc,char **argv)
{
int c_fd;
struct sockaddr_in c_addr;
struct Msg msg;
memset(&c_addr,0,sizeof(struct sockaddr_in));
if(argc != 3 ){
printf("鍙傛暟涓嶅!\n");
exit(-1);
}
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1){
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
perror("connect");
exit(-1);
}
printf("连接成功.......\n");
int mark = 0;
while(1){
memset(msg.cmd,0,sizeof(msg.cmd));
if(mark == 0) printf(">");
gets(msg.cmd);
if(strlen(msg.cmd) == 0){
if(mark == 1){
printf(">");
}
continue;
}
mark = 1;
int ret = cmd_handler(msg,c_fd);
if(ret > IFGO){
putchar('>');
fflush(stdout);
continue;
}
if(ret == -1){
printf("command not\n");
printf(">");
fflush(stdout);
continue;
}
handler_server_message(c_fd,msg);
}
return 0;
}
运行结果:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)