Internet Socket

Posted by Bryan on July 29, 2017

Socket TCP Python实现

连接canet 200T设备

  • canet设备的IP端口为:192.168.0.98:40001
  • 需要把电脑固定IP到同一个网段
   1. sudo gedit /etc/network/interfaces
   2. add the following lines and save:
   auto eth0  
   iface eth0 inet static  
   address 192.168.0.90  
   netmask 255.255.255.0  
   gateway 192.168.0.1
   3. sudo /etc/init.d/networking restart

can to ros / ros to can

  • ROS message经过struct.pack序列化后使用socket发送到某IP的端口上
  • Can报文使用socket接收后,经过struct.unpack反序列化后publish到ROS上

push_frame(can_id, data)

  • 将每针can报文打包为不定长度的字节(13个字节,一个字节8位),append到buffer,
  • 这个过程需要锁定buffer,(如何完成锁定的?buffer_mutex和buffer的关系是如何产生的)

send_buffer

  • 锁定buffer
  • 将buffer掏空,一个字节一个字节的掏空(在此为何一次取50个can报文【13个字节】)
  • data是什么,一次循环加13个字节?
  • dump.txt有什么用?

python struct

  • struct.pack 用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型,可以把这里的字符串理解为字节流,或字节数组),函数原型为:struct.pack(format, var1,var2)

  • struct.unpack 用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组

a = 20  
b = 400   
str = struct.pack("ii", a, b)  
print 'length: ', len(str)          # length:  8  
print str                           # # # #乱码   int==>string stream
print repr(str)                     # '\x14\x00\x00\x00\x90\x01\x00\x00'

str2 = struct.unpack("ii", str)  
print 'length: ', len(str2)          # length:  2  
print str2                           # (20, 400)  流==>int
print repr(str2)                     # (20, 400)  

打包之后的数据所占用的字节数与C语言中的struct十分相似: format对照表

打包的后的字节顺序默认上是由操作系统的决定的,当然struct模块也提供了自定义字节顺序的功能: library对照表

可以查看下列网站寻找 详细信息

Unix Socket Principal Concept

A socket is a thought model and an application programming interface (API). You can create sockets in different “domains” (such as e.g. a “unix socket” or a “internet socket”) and of different types of communication (e.g. a “datagram” socket or a “stream” socket) and talk to different recipients, and everything works exactly the same (well, 99%, there are obviously minute differences taht you will have to account for).
With that in mind, it is clear that sockets do something much more complicated and generally have more overhead than pipes (which are basically no more than a simple memcpy to and from a buffer!), but if you create local sockets (i.e. on the same computer), the operating system usually applies a heavily optimized fast path, so there is really not much of a difference. Sockets are one possible way of inter-process communication (shared memory and pipes being examples of alternatives). All at the same time, they are being used for “networking”, as explained above. When a socket is created, the program has to specify the address domain and socket type. Two process can communicate each other only if their sockets are of the same type and in the same domain.

  • Domain:
    Unix domain, two processes share a common file system communicate. The address in Unix domain is a character string which is basically an entry in file system.
    Internet domain, two processes run on two hosts on the interest communicate. The address in Internet domain referred to IP address(32bit) and a port number(16bit) on that host.
    Each of these has its own address format.
  • Socket type
    Stream sockets, which use TCP (Transmission Control Protocol), which is a reliable, stream oriented protocol.
    Datagram sockets, which use UDP (Unix Datagram Protocol), which is unreliable and message oriented.
    Each uses its own communciations protocol.

Connection socket/datagram socket/symmetric interface

The following procedures discuss detailed situations for connection-oriented model (such as TCP), which can be found in this link. You can access Errors returned for each procedure from the link:
1. Creating sockets
In conclusion, to create a socket, you use socket() function with above properties in header <sys/socket.h>.

sock_fd = socket(domain, type, protocol);
// For the Internet domain, this constant is AF_INET
// If you leave protocol unspecified (a value of 0), 
// the system will select an appropriate protocol
s = socket(AF_INET, SOCK_STREAM, 0);

2. Binding local names
Communicating processes are bound by an “abstraction”. In the Internet domain, an association is composed of local and remote addresses, and local and remote ports. Associations must be unique in most domain. The bind() function allows a process to specify half of an association(local_address, local_port), while the connect() and accept() functions are used to complete a socket’s association (In the Internet domain, there may never be duplicate <local address, local port, remote address, remote port> tuples).
The bound name is a variable-length byte string that’s interpreted by the supporting protocols. Its interpretation may vary from communication domain (Unix or Internet) to communication domain (this is one of the properties that constitute the domain). As mentioned earlier, names in the Internet domain contain an Internet address and port number.

bind( s, name, namelen );
// In binding an Internet address, you use the following sequence: 
#include <sys/types.h>
#include <netinet/in.h>
//...
struct sockaddr_in sin;
//...
bind(s, (struct sockaddr *) &sin, sizeof (sin));

3. Establishing connections
The server, when willing to offer its advertised services, binds a socket to a well-known address associated with the service and then passively listens on its socket.
The client requests services from the server by initiating a connection to the server’s socket. To initiate the connection, the client uses a connect() call. This might appear as:

struct sockaddr_in server;
//...
connect( s, (struct sockaddr *)&server, sizeof (server));

where server (code above) would contain the Internet address and the port number that the client wishes to speak to. If the client’s socket is unbound at the time of the connect call, the system will automatically select and bind a name to the socket if necessary (this is usually how local addresses are bound to a socket).
4. Listening on a socket
To indicate a willingness to listen for connection requests, the server uses a listen() call:

listen(s, 5);

The backlog parameter to the listen() call specifies the maximum number of outstanding connections that may be queued awaiting acceptance by the server process.
5. Accepting a connection
With a socket marked as listening, a server may accept a connection:

struct sockaddr_in from;
// ...
fromlen = sizeof (from);
newsock = accept( s, (struct sockaddr *)&from, &fromlen);

When a connection is accepted, a new descriptor is returned (along with a new socket).
An accept() call normally blocks. It won’t return until a connection is available or until the call is interrupted by a signal to the process.
6. Data transfer
If the peer entity at each end of a connection is anchored, you can send or receive a message without specifying the peer. In this case, you can use the normal read() and write() functions:

write(s, buf, sizeof (buf));
read(s, buf, sizeof (buf));

In addition to read() and write(), you can use the new recv() and send() calls:

send(s, buf, sizeof (buf), flags);
recv(s, buf, sizeof (buf), flags);

Although recv() and send() are virtually identical to read() and write(), the extra flags argument is important (the flag values are defined in <sys/socket.h>). One or more of the following flags may be specified:

MSG_OOB
    Send/receive out-of-band data. 
MSG_PEEK
    Look at data without reading. 
MSG_DONTROUTE
    Send data without routing packets. 

7. Discarding sockets
Once a socket is no longer of interest, it may be discarded by applying close(s) to the descriptor. close() takes place, however, the system will continue to attempt to transfer the undelivered data in a fairly long period of time. If you have no use for pending data and want to shut down the connection immediately, you can perform a shutdown() on the socket prior to closing it with shutdown(s, how). where how is 0 if you’re no longer interested in reading data, 1 if no more data is to be sent, or 2 if no data is to be sent or received.

The above procedures can be summed up with the following:

  1. The steps involved in establishing a socket on the client side are as follows:
    1. Create a socket with the socket() system call
    2. Connect the socket to the address of the server using the connect() system call
    3. Send and receive data. There are a number of ways to do this, but the simplest is to use the read() and write() system calls.
  2. The steps involved in establishing a socket on the server side are as follows:
    1. Create a socket with the socket() system call
    2. Bind the socket to an address using the bind() system call. For a server socket on the Internet, an address consists of a port number on the host machine.
    3. Listen for connections with the listen() system call
    4. Accept a connection with the accept() system call. This call typically blocks until a client connects with the server.
    5. Send and receive data

Connectionless socket/stream socket/asymmetric interface

Datagram facilities are found in contemporary packet-switched networks. Although processes are still likely to be clients and servers, there’s no requirement that connections be established. Instead, each message includes the destination address.