首页 > 代码库 > 网络编程--大杂烩

网络编程--大杂烩

网络编程本质上就是不同机器上的程序之间的通信。

常见的tcp/ip命令配置,socket编程,更高级的封装好的server API等等,都是是网络编程的一部分。

自己也把见过的这些大杂烩理一下。


1. tcp/ip

常见的配置命令在tcp/ip详解第一卷有详细的原理解释和应用实例,而在第二卷中,是代码实现的解释。

linux几个常见的tcp/ip命令: ping, ifconfig, traceroute, netstat, route, ip, host等等

根据类型划分:

网络参数配置类:

ifconfig, ip, route, 这几个命令需要根据需要了解每个参数的含义。


网络侦测类:

ping,netstat, traceroute, host

ping: 是通过icmp包来测试两部主机间的回声和判断状况

traceroute:就是更进一步,诊断追踪两部主机之间通过的各个节点 (node) 通讯状况的好坏

netstat: 就是在观察网络的联机状态了,而网络联机状态中, 又以观察『我目前开了多少的 port 在等待客户端的联机』以及 『目前我的网络联机状态中,有多少联机已创建或产生问题』最常见.

host: 查询某主机的ip



2. socket api及概念

linux c代码里,客户端和服务端编程流程:
服务端:
  socket()建立套接字

  bind()套接字与地址(IP和端口)的绑定

  listen()服务器监听客户端的连接 

  accept()接收客户端连接 

  send(), recv()接收和发送数据 

  close()关闭套接字    

客户端:
  socket()建立套接字

  connect()连接服务器

  send(),recv()读写网络数据 

  close()关闭套接字

从socket的数据结构,就可以看出socket编程,需要设置哪些参数?

IPV4的地址参数:

  typedef uint32_t in_addr_t;
  typedef uint16_t in_port_t;
  typedef unsigned short sa_family_t;
  
  struct in_addr
  {
      in_addr_t s_addr;		//32位IPv4地址,网络字节序
  };
  
  struct sockaddr_in
  {
      uint8_t sin_len;
      sa_family_t sin_family; 		//协议族类型
      in_port_t sin_port;       	//端口号
      struct in_addr sin_addr; 		//IP地址
      char sin_zero[8];          //保留使用
  };

而socketaddr_in又有通用的表示方式

  struct sockaddr {
  uint8_t		s_len;
  sa_family_t	sa_family;
  char			sa_data[14];
  };

而java的实现是客户端使用一个Socket类,服务端使用ServerSocket类

客户端的socket类只需初始化IP和端口,然后发送消息,如

Socket s = new Socket ("198.163.227.6", 13);
InputStream is = s.getInputStream ();
// Read from the stream.
is.close ();
s.close ();

而服务端ServerSocket的初始化只需指明端口,然后便可以监听和接收数据。


而对于UDP编程,也有很多应用场景,比较嵌入式编程中,只需负责发出控制消息,不需要建立稳定链接的场景,可以设置UDP的通信方式,只需在socket协议族和端口类型指明为udp方式。


网上找来的一个例子,java版本:

服务端代码:

package Server;

import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;

public class Server extends JFrame {
  // Text area for displaying contents
  private JTextArea jta = new JTextArea();

  public static void main(String[] args) {
    new Server();
  }

  public Server() {
    // Place text area on the frame
    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(new JScrollPane(jta), BorderLayout.CENTER);

    setTitle("Server");
    setSize(500, 300);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setVisible(true); // It is necessary to show the frame here!

    try {
      // Create a server socket
      ServerSocket serverSocket = new ServerSocket(8000);
      jta.append("Server started at " + new Date() + ‘\n‘);

      // Listen for a connection request
      Socket socket = serverSocket.accept();

      // Create data input and output streams
      DataInputStream inputFromClient = new DataInputStream(
        socket.getInputStream());
      DataOutputStream outputToClient = new DataOutputStream(
        socket.getOutputStream());

      while (true) {
        // Receive radius from the client
        double radius = inputFromClient.readDouble();

        // Compute area
        double area = radius * radius * Math.PI;

        // Send area back to the client
        outputToClient.writeDouble(area);

        jta.append("Radius received from client: " + radius + ‘\n‘);
        jta.append("Area found: " + area + ‘\n‘);
      }
    }
    catch(IOException ex) {
      System.err.println(ex);
    }
  }
}

客户端代码:

package Client;

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Client extends JFrame implements ActionListener {
  // Text field for receiving radius
  private JTextField jtf = new JTextField();

  // Text area to display contents
  private JTextArea jta = new JTextArea();

  // IO streams
  private DataOutputStream outputToServer;
  private DataInputStream inputFromServer;

  public static void main(String[] args) {
    new Client();
  }

  public Client() {
    // Panel p to hold the label and text field
    JPanel p = new JPanel();
    p.setLayout(new BorderLayout());
    p.add(new JLabel("Enter radius"), BorderLayout.WEST);
    p.add(jtf, BorderLayout.CENTER);
    jtf.setHorizontalAlignment(JTextField.RIGHT);

    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(p, BorderLayout.NORTH);
    getContentPane().add(new JScrollPane(jta), BorderLayout.CENTER);

    jtf.addActionListener(this); // Register listener

    setTitle("Client");
    setSize(500, 300);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setVisible(true); // It is necessary to show the frame here!

    try {
      // Create a socket to connect to the server
      Socket socket = new Socket("localhost", 8000);
      // Socket socket = new Socket("130.254.204.36", 8000);
      // Socket socket = new Socket("drake.Armstrong.edu", 8000);

      // Create an input stream to receive data from the server
      inputFromServer = new DataInputStream(
        socket.getInputStream());

      // Create an output stream to send data to the server
      outputToServer =
        new DataOutputStream(socket.getOutputStream());
    }
    catch (IOException ex) {
      jta.append(ex.toString() + ‘\n‘);
    }
  }

  public void actionPerformed(ActionEvent e) {
    String actionCommand = e.getActionCommand();
    if (e.getSource() instanceof JTextField) {
      try {
        // Get the radius from the text field
        double radius = Double.parseDouble(jtf.getText().trim());

        // Send the radius to the server
        outputToServer.writeDouble(radius);
        outputToServer.flush();

        // Get area from the server
        double area = inputFromServer.readDouble();

        // Display to the text area
        jta.append("Radius is " + radius + "\n");
        jta.append("Area received from the server is "
          + area + ‘\n‘);
      }
      catch (IOException ex) {
        System.err.println(ex);
      }
    }
  }
}

3. httpserver编程

IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层。
socket编程是对tcp/ip协议栈的封装,而http是应用层协议,主要是解决如何封装数据。
而对http协议的书面描述是这样的:
“HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。
HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。”
httpserver编程是对socket编程进一步封装。

mongoose是httpserver/web server编程的一种极其方便的实现,常用在嵌入式平台上,能用于restful服务,rpc进程间通信等场景。
更详细的描述见:
http://code.google.com/p/mongoose/

mongoose的API 接口在mongoose.h声明,而mongoose.c是源码实现。

如何使用mongoose?
常用接口:mg_create_server() 创建服务器
 mg_set_option() 配置服务器,如打开端口,设置服务的目录
mg_poll_server() 在循环等待服务完成
mg_destroy_server() 清除服务器


一个简单的例子,对外提供静态目录下的文件服务:

#include "mongoose.h"

int main(void) {
  struct mg_server *server = mg_create_server(NULL, NULL);
  mg_set_option(server, "document_root", ".");      // Serve current directory
  mg_set_option(server, "listening_port", "8080");  // Open port 8080

  for (;;) {
    mg_poll_server(server, 1000);   // Infinite loop, Ctrl-C to stop
  }
  mg_destroy_server(&server);

  return 0;
}

对客户端请求进行响应的例子:

#include <string.h>
#include "mongoose.h"

static int event_handler(struct mg_connection *conn, enum mg_event ev) {
  if (ev == MG_AUTH) {
    return MG_TRUE;   // Authorize all requests
  } else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) {
    mg_printf_data(conn, "%s", "Hello world");
    return MG_TRUE;   // Mark as processed
  } else {
    return MG_FALSE;  // Rest of the events are not processed
  }
}

int main(void) {
  struct mg_server *server = mg_create_server(NULL, event_handler);
  mg_set_option(server, "document_root", ".");
  mg_set_option(server, "listening_port", "8080");

  for (;;) {
    mg_poll_server(server, 1000);  // Infinite loop, Ctrl-C to stop
  }
  mg_destroy_server(&server);

  return 0;
}

更多例子:

http://cesanta.com/docs/Embed.shtml


在java中,jdk提供了httpserver的API,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求。一个Http请求和它的响应称为一个交换,抽象出HttpExchange类, HttpServer负责将HttpExchange传给HttpHandler实现类的回调功能。



4. DBUS编程

不仅对socket进行封装,也不仅仅是一种分布式进程通信方式,高大上的是它拥有一个独立daemon,负责数据和消息的转发。
它使得进程间通信的使用更加方便、好用,不用关心底层的实现细节。
原理是大概如此,更详细的描述去网上搜搜,很多资料。