网络编程

首先介绍,python的socket,就是编程接口(API),对TCP/IP的封装

UDP by python

服务器端:设置好socket后就等着客户端的请求,收到请求后发消息

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)#设置为ipv4,UDO
s.bind(('127.0.0.1', 6666))# 绑定 客户地址&端口:
print ('Build UDP on 6666 port')
while True:
    # 接收数据 自动阻塞 等待客户端请求:
    data, addr = s.recvfrom(1024)
    print ('Received from %s:%s.' % addr)
    s.sendto('Hello, %s!' % data, addr)
#recvfrom()方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用sendto()就可以把数据用UDP发给客户端。

客户端:设置好socket后,发送data,再print接收到的data

#encoding=utf8
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
for data in ['NEUQ', 'Li Haoyuan']:
    # 发送数据:
    s.sendto(data, ('127.0.0.1', 6666))
    # 接收数据:
    print (s.recv(1024))
s.close()

结果:

 

TCP

在UDP基础上需要三次握手保证可靠传输,多了listen,connect等操作

服务器端:首先调用socket函数获得文件描述符并配置socket参数,如端口、协议类型,再为套接字分配本地IP和协议端口。准备完成后进入阻塞状态,通过listen函数等待来自客户端的连接请求。当连接队列中有连接时,返回一个连接并输出连接者IP和端口号。调用send函数给连接者发送一条消息,之后关闭连接和监听。

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  

#define PORT 1234  
#define BACKLOG 1 

int main()
{
	int listenfd, connectfd;
	struct sockaddr_in server;
	struct sockaddr_in client;
	socklen_t addrlen;

	// socket函数 调用socket获得文件描述符
		// 参数:IPV_4协议 接口类型 是否为原始套接口
	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("Socket error\n");
		exit(1);
	}
	int opt = SO_REUSEADDR;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	bzero(&server, sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr.s_addr = htonl(INADDR_ANY);

	// bind函数 为套接口分配本地IP和协议端口
		// 参数:socket返回的描述字 指向协议地址结构的指针 地址结构长度 
	if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1) {
		perror("bind error!\n");
		exit(1);
	}

	// listen函数 等待来自客户端的连接请求
		// 参数:socket 最大连接个数(定为1) 
	if (listen(listenfd, BACKLOG) == -1) {
		perror("listen error!\n");
		exit(1);
	}

	// 从已连接队列中返回一个连接,如果队列为空则睡眠等待
	addrlen = sizeof(client);
	if ((connectfd = accept(listenfd, (struct sockaddr*)&client, &addrlen)) == -1) {
		perror("connect queue error\n");
		exit(1);
	}
	
	// 服务器端显示信息 
	printf("A connection from client's ip is %s, port is %d\n", inet_ntoa(client.sin_addr), htons(client.sin_port));
	
	// 连接方接收信息 
		// send函数用于数据发送 
	send(connectfd, "Hello LHY!\n", 22, 0);
		
	close(connectfd);
	close(listenfd);
	return 0;
}

客户端: 运行时需要指定参数:IP地址,检查完参数数量后,申请socket,并调用connect函数请求和服务器的连接,通过三次握手进行连接。连接成功后,调用recv函数接收服务器端数据,处理数据(在末尾增加终止符)后输出到本地,之后断开连接。

#include<stdio.h>  
#include<stdlib.h>  
#include<unistd.h>  
#include<string.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<netinet/in.h>  
#include<netdb.h>  
#define  PORT 1234  
#define  MAXDATASIZE 100  

int main(int argc, char *argv[])
{
	int  sockfd, num;
	char  buf[MAXDATASIZE];
	struct hostent *he;
	struct sockaddr_in server;

	// 读取主机名及套接字参数
	if (argc != 2) {
		printf("Usage:%s <IP Address>\n", argv[0]);
		exit(1);
	}
	if ((he = gethostbyname(argv[1])) == NULL) {
		printf("Can not get host!\n");
		exit(1);
	}

	// 申请socket
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		printf("Socket error!\n");
		exit(1);
	}
	bzero(&server, sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr = *((struct in_addr *)he->h_addr);

	// 建立连接 TCP三次握手
	if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1) {
		printf("TCP connect Error!\n");
		exit(1);
	}

	// 接收服务器数据
	if ((num = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
		printf("recv data error!\n");
		exit(1);
	}
	buf[num - 1] = '\0';
	printf("Server return message: %s\n", buf);

	close(sockfd);
	return 0;
}

 结果:

ping的实现

参考。首先计算checksum校验和,和前面一样,通过socket套接字,再利用struct字节打包成二进制,最后用select返回套接字的文件描述符的结合,实现Ping程序。

#-*-coding: UTF-8 -*-

import time
import struct
import socket
import select
import sys

def chesksum(data):
    """
    校验
    """
    n = len(data)
    m = n % 2
    sum = 0 
    for i in range(0, n - m ,2):
        sum += (data[i]) + ((data[i+1]) << 8)#传入data以每两个字节(十六进制)通过ord转十进制,第一字节在低位,第二个字节在高位
    if m:
        sum += (data[-1])
    #将高于16位与低16位相加
    sum = (sum >> 16) + (sum & 0xffff)
    sum += (sum >> 16) #如果还有高于16位,将继续与低16位相加
    answer = ~sum & 0xffff
    #主机字节序转网络字节序列(参考小端序转大端序)
    answer = answer >> 8 | (answer << 8 & 0xff00)
    return answer 

    #连接套接字,并将数据发送到套接字

def raw_socket(dst_addr,imcp_packet):
    rawsocket = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.getprotobyname("icmp"))
    send_request_ping_time = time.time()
    #send data to the socket
    rawsocket.sendto(imcp_packet,(dst_addr,80))#imcp_packet是整个要发的包
    return send_request_ping_time,rawsocket,dst_addr

    #request ping

def request_ping(data_type,data_code,data_checksum,data_ID,data_Sequence,payload_body):
    #把字节打包成二进制数据
    imcp_packet = struct.pack('>BBHHH32s',data_type,data_code,data_checksum,data_ID,data_Sequence,payload_body)
    icmp_chesksum = chesksum(imcp_packet)#获取校验和
    icmp_packet = struct.pack('>BBHHH32s',data_type,data_code,icmp_chesksum,data_ID,data_Sequence,payload_body)
    return icmp_packet

    #reply ping

def reply_ping(send_request_ping_time,rawsocket,data_Sequence,timeout = 2):
    while True:
        started_select = time.time()
        what_ready = select.select([rawsocket], [], [], timeout)
        wait_for_time = (time.time() - started_select)
        if what_ready[0] == []:  # Timeout
            return -1
        time_received = time.time()
        received_packet, addr = rawsocket.recvfrom(1024)
        icmpHeader = received_packet[20:28]
        type, code, checksum, packet_id, sequence = struct.unpack(
            ">BBHHH", icmpHeader
        )
        if type == 0 and sequence == data_Sequence:
            return time_received - send_request_ping_time
        timeout = timeout - wait_for_time
        if timeout <= 0:
            return -1

    #实现 ping 主机/ip

def ping(host):
    data_type = 8 # ICMP Echo Request
    data_code = 0 # must be zero
    data_checksum = 0 # "...with value 0 substituted for this field..."
    data_ID = 0 #Identifier
    data_Sequence = 1 #Sequence number
    payload_body = b'NEUQ' #data
    dst_addr = socket.gethostbyname(host)#将主机名转ipv4地址格式,返回以ipv4地址格式的字符串,如果主机名称是ipv4地址,则它将保持不变
    print("正在 Ping {0} [{1}] 具有 32 字节的数据:".format(host,dst_addr))

    for i in range(0,4):
        icmp_packet = request_ping(data_type,data_code,data_checksum,data_ID,data_Sequence + i,payload_body)
        send_request_ping_time,rawsocket,addr = raw_socket(dst_addr,icmp_packet)
        times = reply_ping(send_request_ping_time,rawsocket,data_Sequence + i)
        if times > 0:
            print("来自 {0} 的回复: 字节=32 时间={1}ms".format(addr,int(times*1000)))
            time.sleep(0.7)
        else:
            print("请求超时。")

if __name__ == "__main__":

    # if len(sys.argv) < 2:
    #     sys.exit('Usage: ping.py <host>')
    # ping(sys.argv[1])
    ping("www.baidu.com")

结果:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值