Python中的 socket示例
linux send与recv函数详解
1 #include <sys/socket.h>
2 ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
3 ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
recv 和send的前3个参数等同于read和write。
flags参数值为0或:
flags | 说明 | recv | send |
MSG_DONTROUTE | 绕过路由表查找 | • | |
MSG_DONTWAIT | 仅本操作非阻塞 | • | • |
MSG_OOB | 发送或接收带外数据 | • | • |
MSG_PEEK | 窥看外来消息 | • | |
MSG_WAITALL | 等待所有数据 | • |
1. send解析
sockfd:指定发送端套接字描述符。
buff: 存放要发送数据的缓冲区
nbytes: 实际要改善的数据的字节数
flags: 一般设置为0
1) send先比较发送数据的长度nbytes和套接字sockfd的发送缓冲区的长度,如果nbytes > 套接字sockfd的发送缓冲区的长度, 该函数返回SOCKET_ERROR;
2) 如果nbtyes <= 套接字sockfd的发送缓冲区的长度,那么send先检查协议是否正在发送sockfd的发送缓冲区中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送sockfd的发送缓冲区中的数据或者sockfd的发送缓冲区中没有数据,那么send就比较sockfd的发送缓冲区的剩余空间和nbytes
3) 如果 nbytes > 套接字sockfd的发送缓冲区剩余空间的长度,send就一起等待协议把套接字sockfd的发送缓冲区中的数据发送完
4) 如果 nbytes < 套接字sockfd的发送缓冲区剩余空间大小,send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把套接字sockfd的发送缓冲区中的数据传到连接的另一端的,而是协议传送的,send仅仅是把buf中的数据copy到套接字sockfd的发送缓冲区的剩余空间里)。
5) 如果send函数copy成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR; 如果在等待协议传送数据时网络断开,send函数也返回SOCKET_ERROR。
6) send函数把buff中的数据成功copy到sockfd的改善缓冲区的剩余空间后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个socket函数就会返回SOCKET_ERROR。(每一个除send的socket函数在执行的最开始总要先等待套接字的发送缓冲区中的数据被协议传递完毕才能继续,如果在等待时出现网络错误那么该socket函数就返回SOCKET_ERROR)
7) 在unix系统下,如果send在等待协议传送数据时网络断开,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的处理是进程终止。
2.recv函数
sockfd: 接收端套接字描述符
buff: 用来存放recv函数接收到的数据的缓冲区
nbytes: 指明buff的长度
flags: 一般置为0
1) recv先等待s的发送缓冲区的数据被协议传送完毕,如果协议在传送sock的发送缓冲区中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR
2) 如果套接字sockfd的发送缓冲区中没有数据或者数据被协议成功发送完毕后,recv先检查套接字sockfd的接收缓冲区,如果sockfd的接收缓冲区中没有数据或者协议正在接收数据,那么recv就一起等待,直到把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲区中的数据copy到buff中(注意协议接收到的数据可能大于buff的长度,所以在这种情况下要调用几次recv函数才能把sockfd的接收缓冲区中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的)
3) recv函数返回其实际copy的字节数,如果recv在copy时出错,那么它返回SOCKET_ERROR。如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
4) 在unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用 recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
- //client.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <netinet/in.h>
- #include <fcntl.h>
- #define N 256
- typedef struct sockaddr SA;
- void commd_help();
- void commd_exit();
- void commd_ls(struct sockaddr_in, char *);
- void commd_get(struct sockaddr_in , char *);
- void commd_put(struct sockaddr_in , char *);
- int main(int argc, char *argv[])
- {
- char commd[N];
- struct sockaddr_in addr;
- int len;
- bzero(&addr, sizeof(addr)); //将&addr中的前sizeof(addr)字节置为0,包括'\0'
- addr.sin_family = AF_INET; //AF_INET代表TCP/IP协议
- addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //将点间隔地址转换为网络字节顺序
- addr.sin_port = htons(8989); //转换为网络字节顺序
- len = sizeof(addr);
- while(1)
- {
- printf("ftp>");
- bzero(commd,N);
- //fgets函数从stdin流中读取N-1个字符放入commd中
- if(fgets(commd,N,stdin) == NULL)
- {
- printf("Fgets Error!\n");
- return -1;
- }
- commd[strlen(commd)-1]='\0'; //fgets函数读取的最后一个字符为换行符,此处将其替换为'\0'
- printf("Input Command Is [ %s ]\n",commd);
- if(strncmp(commd,"help",4) == 0) //比较两个字符串前4个字节,若相等则返回0
- {
- commd_help();
- }else if(strncmp(commd, "exit",4) == 0)
- {
- commd_exit();
- exit(0); //结束进程
- }else if(strncmp(commd, "ls" , 2) == 0)
- {
- commd_ls(addr, commd);
- }else if(strncmp(commd, "get" , 3) == 0)
- {
- commd_get(addr, commd);
- }else if(strncmp(commd, "put", 3) ==0 )
- {
- commd_put(addr, commd);
- }else
- {
- printf("Command Is Error!Please Try Again!\n");
- }
- }
- return 0;
- }
- void commd_help()
- {
- printf("\n=------------------- Welcome to Use the Ftp ----------------=\n");
- printf("| |\n");
- printf("| help : Display All Command for the Server |\n");
- printf("| |\n");
- printf("| exit: Quit The Sever |\n");
- printf("| |\n");
- printf("| ls : Display All file On the Ftp Server |\n");
- printf("| |\n");
- printf("| get <file>: Download FIle from the Ftp Server |\n");
- printf("| |\n");
- printf("| put <file>: Upload FIle to the Ftp Server |\n");
- printf("| |\n");
- printf("=-----------------------------------------------------------=\n");
- return ;
- }
- void commd_exit()
- {
- printf("Byte!\n");
- }
- void commd_ls(struct sockaddr_in addr, char *commd)
- {
- int sockfd;
- //创建套接字
- if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
- {
- printf("Socket Error!\n");
- exit(1);
- }
- if(connect(sockfd, (SA *)&addr, sizeof(addr)) < 0)
- {
- printf("Connect Error!\n");
- exit(1);
- }
- //将commd指向的内容写入到sockfd所指的文件中,此处即指套接字
- if(write(sockfd, commd, N) < 0)
- {
- printf("Write Error!\n");
- exit(1);
- }
- while(read(sockfd, commd, N) > 0) //从sockfd中读取N字节内容放入commd中,
- { //返回值为读取的字节数
- printf(" %s ",commd);
- }
- printf("\n");
- close(sockfd);
- return ;
- }
- /**************************************************/
- /*函数功能:实现文件的下载 */
- /**************************************************/
- void commd_get(struct sockaddr_in addr, char *commd)
- {
- int fd;
- int sockfd;
- char buffer[N];
- int nbytes;
- //创建套接字,并进行错误检测
- if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
- {
- printf("Socket Error!\n");
- exit(1);
- }
- //connect函数用于实现客户端与服务端的连接,此处还进行了错误检测
- if(connect(sockfd, (SA *)&addr, sizeof(addr)) < 0)
- {
- printf("Connect Error!\n");
- exit(1);
- }
- //通过write函数向服务端发送数据
- if(write(sockfd, commd, N) < 0)
- {
- printf("Write Error!At commd_get 1\n");
- exit(1);
- }
- //利用read函数来接受服务器发来的数据
- if(read(sockfd, buffer, N) < 0)
- {
- printf("Read Error!At commd_get 1\n");
- exit(1);
- }
- //用于检测服务器端文件是否打开成功
- if(buffer[0] =='N')
- {
- close(sockfd);
- printf("Can't Open The File!\n");
- return ;
- }
- //open函数创建一个文件,文件地址为(commd+4),该地址从命令行输入获取
- if((fd=open(commd+4, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
- {
- printf("Open Error!\n");
- exit(1);
- }
- //read函数从套接字中获取N字节数据放入buffer中,返回值为读取的字节数
- while((nbytes=read(sockfd, buffer, N)) > 0)
- {
- //write函数将buffer中的内容读取出来写入fd所指向的文件,返回值为实际写入的字节数
- if(write(fd, buffer, nbytes) < 0)
- {
- printf("Write Error!At commd_get 2");
- }
- }
- close(fd);
- close(sockfd);
- return ;
- }
- /**************************************************/
- /*函数功能:实现文件的上传 */
- /**************************************************/
- void commd_put(struct sockaddr_in addr, char *commd)
- {
- int fd;
- int sockfd;
- char buffer[N];
- int nbytes;
- //创建套接字
- if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
- {
- printf("Socket Error!\n");
- exit(1);
- }
- //客户端与服务端连接
- if(connect(sockfd, (SA *)&addr, sizeof(addr)) < 0)
- {
- printf("Connect Error!\n");
- exit(1);
- }
- //从commd中读取N字节数据,写入套接字中
- if(write(sockfd, commd, N)<0)
- {
- printf("Wrtie Error!At commd_put 1\n");
- exit(1);
- }
- //open函数从(commd+4)中,读取文件路径,以只读的方式打开
- if((fd=open(commd+4, O_RDONLY)) < 0)
- {
- printf("Open Error!\n");
- exit(1);
- }
- //从fd指向的文件中读取N个字节数据
- while((nbytes=read(fd, buffer, N)) > 0)
- {
- //从buffer中读取nbytes字节数据,写入套接字中
- if(write(sockfd, buffer, nbytes) < 0)
- {
- printf("Write Error!At commd_put 2");
- }
- }
- close(fd);
- close(sockfd);
- return ;
- }
下面server.c中后面有些注释没加,感觉功能上跟client相似,就没加,可以参看前面的
- //server.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <dirent.h>
- #include <fcntl.h>
- #define N 256
- typedef struct sockaddr SA;
- void commd_ls(int);
- void commd_get(int, char *);
- void commd_put(int, char *);
- int main(int arg, char *argv[])
- {
- int ser_sockfd,cli_sockfd;
- struct sockaddr_in ser_addr,cli_addr;
- int ser_len, cli_len;
- char commd [N];
- bzero(commd,N);//将commd所指向的字符串的前N个字节置为0,包括'\0'
- if((ser_sockfd=socket(AF_INET, SOCK_STREAM, 0) ) < 0)
- {
- printf("Sokcet Error!\n");
- return -1;
- }
- bzero(&ser_addr,sizeof(ser_addr));
- ser_addr.sin_family = AF_INET;
- ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);//在TCP连接中,此处类似于自动获取ip地址
- //在绑定ip时,自动选择ip地址
- ser_addr.sin_port = htons ( 8989 );
- ser_len = sizeof(ser_addr);
- //将ip地址与套接字绑定
- if((bind(ser_sockfd, (SA *)&ser_addr, ser_len)) < 0)
- {
- printf("Bind Error!\n");
- return -1;
- }
- //服务器端监听
- if(listen(ser_sockfd, 5) < 0)
- {
- printf("Linsten Error!\n");
- return -1;
- }
- bzero(&cli_addr, sizeof(cli_addr));
- ser_len = sizeof(cli_addr);
- while(1)
- {
- printf("server_ftp>");
- //服务器端接受来自客户端的连接,返回一个套接字,此套接字为新建的一个,并将客户端的地址等信息存入cli_addr中
- //原来的套接字仍处于监听中
- if((cli_sockfd=accept(ser_sockfd, (SA *)&cli_addr, &cli_len)) < 0)
- {
- printf("Accept Error!\n");
- exit(1);
- }
- //由套接字接收数据时,套接字把接收的数据放在套接字缓冲区,再由用户程序把它们复制到用户缓冲区,然后由read函数读取
- //write函数同理
- if(read(cli_sockfd, commd, N) < 0) //read函数从cli_sockfd中读取N个字节数据放入commd中
- {
- printf("Read Error!\n");
- exit(1);
- }
- printf("recvd [ %s ]\n",commd);
- if(strncmp(commd,"ls",2) == 0)
- {
- commd_ls(cli_sockfd);
- }else if(strncmp(commd,"get", 3) == 0 )
- {
- commd_get(cli_sockfd, commd+4);
- }else if(strncmp(commd, "put", 3) == 0)
- {
- commd_put(cli_sockfd, commd+4);
- }else
- {
- printf("Error!Command Error!\n");
- }
- }
- return 0;
- }
- void commd_ls(int sockfd)
- {
- DIR * mydir =NULL;
- struct dirent *myitem = NULL;
- char commd[N] ;
- bzero(commd, N);
- //opendir为目录操作函数,类似于open函数
- //mydir中存有相关目录的信息(有待学习)
- if((mydir=opendir(".")) == NULL)
- {
- printf("OpenDir Error!\n");
- exit(1);
- }
- while((myitem = readdir(mydir)) != NULL)
- {
- if(sprintf(commd, myitem->d_name, N) < 0)
- {
- printf("Sprintf Error!\n");
- exit(1);
- }
- if(write(sockfd, commd, N) < 0 )
- {
- printf("Write Error!\n");
- exit(1);
- }
- }
- closedir(mydir);
- close(sockfd);
- return ;
- }
- void commd_get(int sockfd, char *filename)
- {
- int fd, nbytes;
- char buffer[N];
- bzero(buffer, N);
- printf("get filename : [ %s ]\n",filename);
- if((fd=open(filename, O_RDONLY)) < 0)
- {
- printf("Open file Error!\n");
- buffer[0]='N';
- if(write(sockfd, buffer, N) <0)
- {
- printf("Write Error!At commd_get 1\n");
- exit(1);
- }
- return ;
- }
- buffer[0] = 'Y'; //此处标示出文件读取成功
- if(write(sockfd, buffer, N) <0)
- {
- printf("Write Error! At commd_get 2!\n");
- close(fd);
- exit(1);
- }
- while((nbytes=read(fd, buffer, N)) > 0)
- {
- if(write(sockfd, buffer, nbytes) < 0)
- {
- printf("Write Error! At commd_get 3!\n");
- close(fd);
- exit(1);
- }
- }
- close(fd);
- close(sockfd);
- return ;
- }
- void commd_put(int sockfd, char *filename)
- {
- int fd, nbytes;
- char buffer[N];
- bzero(buffer, N);
- printf("get filename : [ %s ]\n",filename);
- if((fd=open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
- {
- printf("Open file Error!\n");
- return ;
- }
- while((nbytes=read(sockfd, buffer, N)) > 0)
- {
- if(write(fd, buffer, nbytes) < 0)
- {
- printf("Write Error! At commd_put 1!\n");
- close(fd);
- exit(1);
- }
- }
- close(fd);
- close(sockfd);
- return ;
- }
一、 服务器server的写法:
1. 创建 socket 套接字:
网络编程接口 socket(family = AF_INET , type = SOCKET_STREM,proto = 0, fileno = None) 提供了多种socket family。AF_INET 是默认的family,需要绑定IP和端口。 127.0.0.1是一个特殊的IP地址,表示本机地址。如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。
不同的协议family使用的地址形式不同,通常来说我们使用的是AF_INET-----地址是以(IP,PORT)形式存。在type 类型,我们常用的有两种 SOCKET_STREM ,流式套接字,表示基于连接的TCP套接字,SOCKET_DGRAM, 数据报套接字,基于无连接的(UDP)接口。 如果不设定,默认就是 SOCKET_STREM 。
2. bind 绑定
使用方法:socket.bind(address) 把套接字绑定在 address 上,address的形式 根据 family 来设定。不管是client还是server,创建socket的时候都是通过一个本地的文件来进行的。
3. listen 监听
使用方法:socket.listen([backlog]) 使能 socket 接收连接请求,listen(self,backlog = None) ,backlog需要大于0,指定了可以缓存的连接请求的数量。
4. accept 接受请求连接
在等待一个即将来临的连接,会返回一个代表连接的新的socket,还会返回一个地址(host和port),可以用两个东西接收,前一个代表新的socket,后一个就是接收地址。
写的方法:connet_socket,client_addr = srv.accept() connet_socket就是新的socket,然后connet_socket开始接下来的传输。connet_socket,client_addr,前者表示接收的新的socket,后者就是地址,具体看程序第10行。
5. 接收数据
使用方法:socket.recv(bufsize[,flags]) 从 socket 中接收数据,返回的是 bytes ,是接收到的内容。bufsize指定了一次最多接收多少个数据,如果没有数据接收,程序会阻塞,一直到有数据或者远程终端断开连接.
6. 发送数据
使用方法:socket.send(bytes[, flags]) 你的socket必须和远程的socket建立了联系,返回值是发送的数量,可以判断你的数据是否发送完毕,如果没有,继续send余下来的数据
123456789101112131415import
socket
hostname
=
'127.0.0.1'
#设置主机名
port
=
6666
#设置端口号 要确保这个端口号没有被使用,可以在cmd里面查看
addr
=
(hostname,port)
srv
=
socket.socket()
#创建一个socket
srv.bind(addr)
srv.listen(
5
)
print
(
"waitting connect"
)
while
True
:
connect_socket,client_addr
=
srv.accept()
print
(client_addr)
recevent
=
connect_socket.recv(
1024
)
print
(
str
(recevent,encoding
=
'gbk'
))
connect_socket.send.send(bytes(
"你好,数据传输完成,这里是gaby-yan--server"
,encoding
=
'gbk'
))
connect_socket.close()
二、 客户端client的写法:
客户端的写法相对比较简单,只有
1.创建socket
2. 建立连接 connect
3. 发送 send
4. 接收recv
这是由于他们的传递编程框架不同造成的,如图。
12345678910111213import
socket
hostname
=
'127.0.0.1'
port
=
7777
addr
=
(hostname,port)
clientsock
=
socket.socket()
## 创建一个socket
clientsock.connect(addr)
# 建立连接
say
=
input
(
"输入你想传送的消息:"
)
clientsock.send(bytes(say,encoding
=
'gbk'
))
#发送消息
recvdata
=
clientsock.recv(
1024
)
#接收消息 recvdata 是bytes形式的
print
(
str
(recvdata,encoding
=
'gbk'
))
# 我们看不懂bytes,所以转化为 str
clientsock.close()
注意:先运行server的代码,再运行client的代码。
Python中的 socket示例的更多相关文章
- python中的tcp示例详解
python中的tcp示例详解 目录 TCP简介 TCP介绍 TCP特点 TCP与UDP的不同点 udp通信模型 tcp客户端 tcp服务器 tcp注意点 TCP简介 TCP介绍 TCP协议 ...
- python socket编程---从使用Python开发一个Socket示例说到开发者的思维和习惯问题
今天主要说的是一个开发者的思维和习惯问题. 思维包括编程的思维和解决一个具体问题的分析思维,分析思路,分析方法,甚至是分析工具. 无论是好习惯还是不好的习惯,都是在者一天一天的思维中形成的.那些不好的 ...
- python进阶---Python中的socket编程
初识socket编程 一.前言 socket基于C\S架构(客户端\服务端)的编程模型,在Python中是以socket模块存在的. Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是 ...
- Python中的socket网络模块
目录 Socket 服务端(server.py) 客户端(client.py) socket中的一些常用方法 Socket 对象(内建)方法 Python Internet 模块 Python3 提供 ...
- Python中的socket 模块
Python 提供了两个基本的 socket 模块.第一个是 Socket,它提供了标准的 BSD Sockets API.第二个是 SocketServer, 它提供了服务器中心类,可以简化网络服务 ...
- Python中Mock的示例(转)
原文:https://segmentfault.com/a/1190000008753754 一些常用的mock示例 先简单定义个类,方便举例: class Person: def __init__( ...
- python中的socket模块
熟悉了一下python的socket模块,感觉还是有点好玩的,不过坑也也是不少的. 1.服务器端代码 #!/usr/bin/env python import socket HOST='192.168 ...
- 在python中编写socket服务端模块(二):使用poll或epoll
在linux上编写socket服务端程序一般可以用select.poll.epoll三种方式,本文主要介绍使用poll和epoll编写socket服务端模块. 使用poll方式的服务器端程序代码: i ...
- Python中的socket网络编程(TCP/IP,UDP)讲解
在网络编程中的一个基本组件就是套接字(socket).套接字基本上是两个端点的程序之间的"信息通道".程序可能分布在不同的计算机上,通过套接字互相发送信息.套接字包括两个:服务器套 ...
随机推荐
- 【65】Mybatis详解
Mybatis介绍 MyBatis是一款一流的支持自定义SQL.存储过程和高级映射的持久化框架.MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去设置参数和获取检索结果.MyBatis能够 ...
- 网站开发进阶(一)Tomcat域名或IP地址访问方式配置方法
Tomcat域名或IP地址访问方式配置方法 1.配置www.***.com域名方式访问 在Tomcat下面配置域名(如:www.***.com)的时候,同时又不希望客户通过我们网站的IP或者域名访问到 ...
- “《编程珠玑》(第2版)第2章”:A题(二分搜索)
A题是这样子的: 给定一个最多包含40亿个随机排列的32位整数的顺序文件,找出一个不在文件中的32位整数(在文件中至少缺失一个这样的数据——为什么?).在具有足够内存的情况下,如何解决该问题?如果有几 ...
- Android UI技巧(一)——Android中伸缩自如的9patch图片切法,没有美工自给自足
Android UI技巧(一)--Android中伸缩自如的点9图片切法,没有美工自给自足 相信大家对.9 图片应该都很熟悉吧,有些人可能自己都会了,此篇献给那些不会的同学,咱们一起来聊聊.9图片的切 ...
- 第一个Polymer应用 - (1)创建APP结构
原文链接: Step 1: Creating the app structure翻译日期: 2014年7月5日翻译人员: 铁锚在本节中,将使用一些预先构建好的Polymer元素来创建基本的应用程序结构 ...
- Oracle Global Finanicals Technical Reference(二)
Skip Headers Oracle Global Finanicals Oracle Global Financials Technical Reference Manual Release 11 ...
- 史上最简单的C语言链表实现,没有之一
#include <stdio.h> #include <string.h> #include <stdlib.h> #define NR(x) (sizeof(x ...
- Sencha touch API
Sencha touch API http://docs.sencha.com/touch/2.3.1/#!/guide/getting_started
- RHEL6 不重启扫描新添加硬盘
First find your host bus number grep mpt /sys/class/scsi_host/host?/proc_name Which should return a ...
- redis删除所有key
flushdb 删除当前数据库的所有keyflushall 删除所有数据库的所有keydbsize 返回当前数据库的key的数量