• Không có kết quả nào được tìm thấy

Socket Construction

Trong tài liệu C# Network Programming (Trang 123-133)

The core of the System.Net.Sockets namespace is the Socket class. It provides the C# managed code implementation of the Winsock API. The Socket class constructor is as follows:

Socket(AddressFamily af, SocketType st, ProtocolType pt)

As you can see, the basic format of the Socket constructor mimics the original Unix socket() function. It uses three parameters to define the type of socket to create:

An AddressFamily to define the network type

A SocketType to define the type of data connection

A ProtocolType to define a specific network protocol

Each of these parameters is represented by a separate enumeration within the System.Net_.Sockets namespace.

Each enumeration contains the values that can be used. For normal IP communications on networks, the AddressFamily.InterNetwork value should always be used for the AddressFamily. With the InterNetwork

AddressFamily, the SocketType parameter must match a particular ProtocolType parameter. You are not allowed to mix and match SocketTypes and ProtocolTypes. Table 3.7 shows the combinations that can be used for IP communications.

Table 3.7: IP Socket Definition Combinations

SocketType Protocoltype Description

Dgram Udp Connectionless communication

Stream Tcp Connection-oriented communication

Raw Icmp Internet Control Message Protocol

Raw Raw Plain IP packet communication

Using the enumeration values makes it easy to remember all the options (though it does make for some fairly long

Socket() statements!). For example:

Socket newsock = Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

Several properties of the Socket class can be used to retrieve information from a created Socket object, as described in Table 3.8.

Table 3.8: Socket Properties

Property Description

AddressFamily Gets the address family of the Socket

Available Gets the amount of data that is ready to be read

Blocking Gets or sets whether the Socket is in blocking mode

Connected Gets a value that indicates if the Socket is connected to

a remote device

Handle Gets the operating system handle for the Socket

LocalEndPoint Gets the local EndPoint object for the Socket

ProtocolType Gets the protocol type of the Socket

RemoteEndPoint Gets the remote EndPoint information for the Socket

SocketType Gets the type of the Socket

Note

All of the Socket class properties except the LocalEndPoint and RemoteEndPoint are available for a socket immediately after it is created.

The LocalEndPoint and

RemoteEndPoint properties can only be used on bound sockets.

Listing 3.3 is a simple program that demonstrates the Socket properties. Because the LocalEndPoint property needs a bound Socket object, I used the Bind() method to bind the socket to the loopback address of the system

(127.0.0.1).

Listing 3.3: The SockProp.cs sample socket properties program

using System;

using System.Net;

using System.Net.Sockets;

class SockProp {

public static void Main ()

IPAddress ia = IPAddress.Parse("127.0.0.1");

IPEndPoint ie = new IPEndPoint(ia, 8000);

Socket test = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

Console.WriteLine("AddressFamily: {0}", test.AddressFamily);

Console.WriteLine("SocketType: {0}", test.SocketType);

Console.WriteLine("ProtocolType: {0}", test.ProtocolType);

Console.WriteLine("Blocking: {0}", test.Blocking);

test.Blocking = false;

Console.WriteLine("new Blocking: {0}",test.Blocking);

Console.WriteLine("Connected: {0}", test.Connected);

test.Bind(ie);

IPEndPoint iep = (IPEndPoint)test.LocalEndPoint;

Console.WriteLine("Local EndPoint: {0}", iep.ToString());

test.Close();

} }

The output of the program should look like this:

C:\>SockProp

AddressFamily: InterNetwork SocketType: Stream

ProtocolType: Tcp Blocking: True New Blocking: False Connected: False

Local EndPoint: 127.0.0.1:8000 C:\>

By setting the Blocking property to false, you can use non-blocking sockets, similar to the Unix fcntl() function but more on that later.

Socket Options

Like Unix sockets, .NET sockets allow you to set protocol options for the created Socket object. However, because C# is an object-oriented language, there is a twist.

Instead of being a stand-alone function, the call is a method. Like its Unix counterpart, SetSocketOption() configures the socket parameters that you want to tweak to customize the communication parameters. The SetSocketOption() method is overloaded, using three different formats:

SetSocketOption(SocketOptionLevel sl, SocketOptionName sn, byte[] value) SetSocketOption(SocketOptionLevel sl, SocketOptionName sn,int value) SetSocketOption(SocketOptionLevel sl, SocketOptionName sn, object value)

The parameters used are similar to the Unix setsockopt() function. The sl defines the type of socket option to set.

Table 3.9 lists the available SocketOptionLevels.

Table 3.9: SocketOptionLevel Values

Value Description

IP Options for IP sockets

Socket Options for the socket

Tcp Options for TCP sockets

Udp Options for UDP sockets

The sn defines the specific socket option that will be set within the SocketOptionLevel. Table 3.10 lists the available SocketOptionNames.

Table 3.10: SocketOptionName Values

Value SocketOptionLevel Description

AcceptConnection Socket If true, socket is in listening

mode

AddMembership IP Adds an IP group

membership

AddSourceMembership IP Joins a source group

BlockSource IP Blocks data from a source

Broadcast Socket If true, permits sending

broadcast messages

BsdUrgent IP Uses urgent data (can only

be set once and cannot be turned off)

ChecksumCoverage Udp Sets or gets UDP checksum coverage

Debug Socket Records debugging

information if true

DontFragment IP Doesn t fragment the IP

packet

DontLinger Socket Closes socket gracefully

without waiting for data

DontRoute Socket Sends packet directly to

interface addresses

DropMembership IP Drops an IP group

membership

DropSourceMembership IP Drops a source group

Error Socket Gets and clears the error

status

ExclusiveAddressUse Socket Enables a socket to be

bound for exclusive access

Expedited IP Uses expedited data (can

only be set once, and cannot turned off)

HeaderIncluded IP Indicates that the data sent

to the socket will include the IP header

IPOptions IP Specifies IP options to be

used in outbound packets

IpTimeToLive IP Sets the IP packet time-to-live value

KeepAlive Socket Sends TCP keep-alive

packets

Linger Socket Waits after closing the

socket for any extra data

MaxConnections Socket Sets the maximum queue

length used

MulticastInterface IP Sets the interface used for

multicast packets

MulticastLoopback IP IP multicast loopback

MulticastTimeToLive IP Sets the IP multicast time to

live

NoChecksum Udp Sends UDP packets with

checksum set to zero

NoDelay Tcp Disables the Nagle algorithm

for TCP packets

OutOfBandInline Socket Allows receiving

out-of-band data

PacketInformation IP Returns information about

received packets

ReceiveBuffer Socket Sets the total per-socket

buffer reserved for receiving packets

ReceiveLowWater Socket Receives low water mark

ReceiveTimeout Socket Receives time-out

ReuseAddress Socket Allows the socket to be

bound to a port address that is already in use

SendBuffer Socket Sets the total per-socket

buffer reserved for sending packets

SendLowWater Socket Sends low water mark

SendTimeout Socket Sends timeout value

Type Socket Gets socket type

TypeOfService IP Sets the IP type-of-service

field

UnblockSource IP Sets the socket to

non-blocking mode

UseLoopback Socket Bypasses the network

interface when possible

The value parameter defines the value of the socket option name to use. The format of the value is different depending on the SocketOptionName used.

Once the socket is created and modified, you are ready to either wait for incoming connections, or connect to remote devices. The following sections describe how to communicate using the C# Socket class, using connection-oriented and connectionless communication sockets in C#.

Using Connection-Oriented Sockets

Once again, the .NET Framework concepts are similar to Unix network programming. In the .NET Framework, you

can create connection-oriented communications with remote hosts across a network. Because C# is an

object-oriented language, all the Unix socket functions are implemented as methods of the Socket class. By referring to the method from the Socket instance, you can perform network operations using the indicated socket.

Note

This section describes the C# methods in the Sockets class that are used for connection-oriented communication.

Chapter 5, "Connection-Oriented Sockets" will expand on this explanation, showing much more detailed information along with lots of examples.

The Server Functions

Similar to the Unix server, once a server socket is created, it must be bound to a local network address on the system. The Bind() method is used to perform this function:

Bind(EndPoint address)

The address parameter must point to a valid IPEndPoint instance, which includes a local IP address and a port number. After the socket is bound to a local address, you use the Listen() method to wait for incoming connection attempts from clients:

Listen(int backlog)

The backlog parameter defines the number of connections that the system will queue, waiting for your program to service. Any attempts by clients beyond that number of waiting connections will be refused. You should remember that specifying a large number here might have performance consequences for your server. Each pending connection attempt uses buffer space in the TCP buffer area. This means less buffer space available for sent and received packets.

After the Listen() method is performed, the server is ready to accept any incoming connections. This is done with the Accept() method. The Accept() method returns a new socket descriptor, which is then used for all communication calls for the connection.

Here s a sample of C# server code that sets up the necessary socket pieces:

IPHostEntry local = Dns.GetHostByName(Dns.GetHostName());

IPEndPoint iep = new IPEndPoint(local.AddressList[0], 8000);

Socket newserver = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

newserver.Bind(iep);

newserver.Listen(5);

Socket newclient = newserver.Accept();

This program will block at the Accept() statement, waiting for a client connection. Once a client connects to the server, the newclient Socket object will contain the new connection information and should be used for all communication with the remote client. The newserver Socket object will still be bound to the original IPEndPoint object and can be used to accept more connections with another Accept() method. If no more Accept() methods are called, the server will not respond to any more client connection attempts.

After the client connection has been accepted, the client and server can begin transferring data. The Receive() and Send() methods are used to perform this function. Both of these methods are overloaded with four forms of the

method. Table 3.11 shows the available methods to use for each.

Table 3.11: The Receive() and Send() Methods

Method Description

Receive(byte[] data) Receives data and places it in the specified byte array

Receive(byte[] data, SocketFlags sf) Sets socket attributes, receives data, and places it in the specified byte array

Receive(byte[] data, int size, SocketFlags sf) Sets socket attributes, receives the specified size of data, and places it in the specified byte array

Receive(byte[] data, int offset, int size, SocketFlags sf) Sets socket attributes, receives the size bytes of data, and stores it at offset offset in the data byte array

Send(byte[] data) Sends the data specified in the byte array

Send(byte[] data, SocketFlags sf) Sets socket attributes and sends the data specified in the bytes array

Send(byte[] data, int size, SocketFlags sf) Sets socket attributes and sends the specified size of data in the specified byte array

Send(byte[] data, int offset, int size, SocketFlags sf) Sets socket attributes and sends size bytes of data starting at offset offset in the data byte array

The simple form of the Send() and Receive() methods sends a single byte array of data, or receives data and places it into the specified byte array. If you want to specify any special SocketFlags, you can add them into the method call as well. Table 3.12 shows the available SocketFlag values.

Table 3.12: SocketFlag Values

Value Description

DontRoute Sends data without using the internal routing tables

MaxIOVectorLength Provides a standard value for the number of WSABUF structures used to send and receive data

None Uses no flags for this call

OutOfBand Processes out-of-band data

Partial Partially sends or receives message

Peek Only peeks at the incoming message

The other parameters available on the Receive() and Send() methods allow you to specify how many bytes of data to send or receive and where in the buffer you want the data. This will be demonstrated in much greater detail in Chapter 5.

The Client Functions

The client device must also bind an address to the created Socket object, but it uses the Connect() method rather than Bind(). As with Bind(),Connect()requires an IPEndPoint object for the remote device to which the client needs to connect:

IPAddress host = IPAddress.Parse("192.168.1.1");

IPEndPoint hostep = new IPEndPoint(host, 8000);

Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

sock.Connect(hostep);

The Connect() method will block until the connection has been established. If the connection cannot be established, it will produce an exception (see the section "Socket Exceptions" later in this chapter).

Once the connection has been established, the client can use the Send() and Receive() methods of the Socket classsimilar to the way the server uses them. When communication is done, the Socket instance must be closed. Like the Unix socket, the Socket class usesboth a shutdown() method to gracefully stop a session, and a close() method to actually close the session. The shutdown() method uses one parameter to determine how the socket will shutdown.

Available values for Socket.Shutdown() are described in Table 3.13.

Table 3.13: Socket.Shutdown() Values

Value Description

SocketShutdown.Both Prevents both sending and receiving data on the socket

SocketShutdown.Receive Prevents receiving data on the socket. An RST will be sent if additional data is received.

SocketShutdown.Send Prevents sending data on the socket. A FIN will be sent after all remaining buffer data is sent.

Here is the typical way to gracefully close a connection:

sock.Shutdown(SocketShutdown.Both);

sock.Close();

This allows the Socket object to gracefully wait until all data has been sent from its internal buffers.

Using Connectionless Sockets

.NET uses the same functionality for connectionless sockets as that employed by the Unix model. When you create a socket with the SocketType.Dgram socket type, the UDP protocol is used to transmit packets across the network.

Similar to the Unix model, you must set up the Bind() method for the server to bind the socket to a particular port.

Also similar to the Unix model, the server and client do not need to use the Listen() or Connect() methods.

Because there is no connection for communication, the standard Receive() and Send() methods will not work.

Instead, you must use the special ReceiveFrom() and SendTo() methods. The formats for these methods comprise the same base parameters as the Receive() and Send() methods (as seen in Table 3.11). In addition, there is an extra parameter that is a reference to an EndPoint object. This parameter defines where the data is going (for SendTo) or where it is coming from (for ReceiveFrom). For example, the simplest format of the methods would be as follows:

ReceiveFrom(byte[], ref EndPoint) SendTo(byte[], ref EndPoint)

For UDP communications, the EndPoint object will point to an IPEndPoint object. If you are new to C#, you may not have seen the ref keyword before. The ref keyword indicates that the method will access the EndPoint object by reference in memory, and not by its value. This is a popular technique in C and C++ programming, but it is not seen very often in C# programs.

Trong tài liệu C# Network Programming (Trang 123-133)