⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 article711.asp.htm

📁 一个网络游戏程序编写的例子
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<P>To initialize WinSock, you call WSAStartup(). The first parameter of
WSAStartup() gives the highest version of WinSock that your program can
use. The library then uses a decision table based on the version given and
the versions supported to decide whether or not it can effectively support
your application. Parameter 1, the version requested, is a WORD. WinSock
versions that you can request include 1.0, 1.1, and 2.0 (at present). Use
the MAKEWORD macro to form the version for the first parameter, as follows:
MAKEWORD(2,0) = version 2.0. For simplicity and maximal compatibility, it's
a good idea to request the earliest version of WinSock that supports what
you need to do.

<P>The second parameter of WSAStartup() is a pointer to a WSADATA struct to
get the specific initialization information. The WSADATA structure will
contain information about the actual version of WinSock that you've gotten
a hold of, as well as some limits that might come in handy for something
and might not.

<P>The return value of WSAStartup() is an error code. If the error code is 0,
then obviously there was no error and we can proceed to use the WinSock
library. The constants representing error codes usually take the form
WSAEx, where x is the specific descrition of the error.

<BLOCKQUOTE><PRE><FONT FACE="courier new, fixedsys" SIZE="2" COLOR="#000088">
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 0), &wsaData)) {
   ErrorMessage("Error while attempting to initialize WinSock");
}

</PRE></BLOCKQUOTE>

<P><B>Creating Sockets</B>

<P>To create a socket, you call either socket() (real shock, eh?) or
WSASocket(). As is the case with many WinSock functions, the fancy WSA
prefix functions are easily distinguished from their counterparts preserved
for portability. In this tutorial, I will cover the preserved Berkley forms
of the functions in WinSock for the most part. Why? Because most of them
don't have "extensions" built in, and are consequentially easier to
understand.

<P>The first parameter of socket() specifies the address family that a socket
will use. This is related to the way/place in which data transferred over
the socket will be sent/will go. For the purpose of Internet-based TCP/IP
programming, AF_INET will work. However, if you feel like having fun, check
out the AF_x line of constants in winsock.h or winsock2.h. You might be
surprised at the sheer number of networks that Winsock can be applied to.

<P>The second parameter of socket() is the socket type. SOCK_STREAM will form
a stream-based socket (surprise surprise) that supports bidirectional data
transfer and an internally managed transfer buffer, and is generally more
reliable than other socket types, such as SOCK_DGRAM. Check out the SOCK_x
line of constants in winsock2.h for more of these socket types (if you have
Winsock 2.0...1.0 and 1.1 support only the two mentioned above).

<P>The third parameter of socket() is the protocol that the socket is to use.
These take the form IPPROTO_x. IPPROTO_TCP, for instance, specifies the TCP
protocol. IPPROTO_UDP specifies the UDP protocol. IPPROTO_IP specifies the
IP protocol (right...). Once again, consult your handy header file for more
information.

<P>socket() returns a handle to the newly created socket, an integer (SOCKET
is #defined as a u_int). In fancy-talk, this integer is called a socket
descriptor.

<BLOCKQUOTE><PRE><FONT FACE="courier new, fixedsys" SIZE="2" COLOR="#000088">
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

</PRE></BLOCKQUOTE>

<P><B>Binding and Connecting</B>

<P>Immediately after its creation, a socket can do nothing. Before it can do
anything, we must define which port number a socket is to pay attention to
and which host that port is on: That is, we must actually associate our
socket with a socket! To attach a socket to a port on the local machine,
use the bind() function. To connect a socket to a socket on a remote host,
use the connect() function.

<P>There is a crucial distinction that needs to be made between bind() and
connect(). First and foremost, as has already been stated, bind() is used
to attach a socket to a specific port on the local machine, and this only
before connection. If you attempt to connect() without binding a socket to
a port on the local machine, connect() will bind it randomly for you. If
you attempt to bind() while connected, you will be doing something very
strange. The second distinction is that bind() is usually used in
conjunction with listen(), which listens for a connection on a given
socket. This does not mean that bind() has to be used in this manner.
Seldom does one have to bind to a specific local port when performing a
transaction with a remote machine. The similarity between bind() and
connect() would be that they have effectively the same parameter list with
the middle parameter replaced with the local host's address in bind().

<P>The first parameter of connect()/bind() is a socket descriptor. The second
parameter is a pointer to a sockaddr struct. This parameter indicates what
the socket is to be bound to/connect to, i.e., what machine we're dealing
with. This is what a sockaddr looks like:

<BLOCKQUOTE><PRE><FONT FACE="courier new, fixedsys" SIZE="2" COLOR="#000088">
struct sockaddr {
        u_short sa_family;
        char sa_data[14];
};

</PRE></BLOCKQUOTE>

<P>sa_family is yet another instance of the family things that we've been
seeing; the AF_x line of constants can be used to specify this. This family
will more likely than not be the same one used in the creation of the
socket. In fact, if it's not, give me a ring, because that would be
extremely unusual. sa_data contains the address of the remote machine.

<P>One might ask what char sa_data[14] really means, or indeed, what this
whole structure really means; that is, how the address is really formatted.
In TCP/IP networks, the above structure can be rewritten like this:

<BLOCKQUOTE><PRE><FONT FACE="courier new, fixedsys" SIZE="2" COLOR="#000088">
struct sockaddr_in {
       short sin_family;
       u_short sin_port;
       struct in_addr sin_addr;
       char sin_zero[8];
};

</PRE></BLOCKQUOTE>

<P>(note short instead of u_short; this, I can only assume, is an error in the
published WinSock documentation, upon which I have based these structure
listings in order to avoid unintentionally introducing any further errata.)

<P>For the overly curious, in_addr looks like this:

<BLOCKQUOTE><PRE><FONT FACE="courier new, fixedsys" SIZE="2" COLOR="#000088">
struct in_addr {
                union {
                        struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
                        struct { u_short s_w1,s_w2; } S_un_w;
                        u_long S_addr;
                } S_un;
};

</PRE></BLOCKQUOTE>

<P>And now you want to know why you care. Well, if you'll think back to our
discussion on how IP datagrams are routed from one place to another, you'll
recall the concept of an Internet address that uniquely identifies a host
on a network. sockaddr_in specifies an address for a unique port on a
unique host. in_addr is the actual Internet address of that host. You will
note four bytes, referenced in the union through chars, shorts, and longs,
just as I promised back in the IP discussion (four octets). But we don't
need to worry about things on this level. That's just how it fits together.

<P>The third and final parameter of bind()/connect() is the size of the
address structure given as the second parameter. This is most commonly
specified using the sizeof keyword in conjunction with the actual address
structure. So, for instance:

<BLOCKQUOTE><PRE><FONT FACE="courier new, fixedsys" SIZE="2" COLOR="#000088">
struct sockaddr_in addr;

...

SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

//assume that we fill addr somewhere in here

...

connect(s, (struct sockaddr *)&addr, sizeof(addr));

...

</PRE></BLOCKQUOTE>

<P>That's the general idea.

<P><B>Addressing and Name Service Support</B>

<P>It should be noted that sockaddr_in is a wrapper that identifies an IP
address and a port on the machine identified by that IP address for
practical TCP/IP development purposes. The very important question of how
we know this IP address comes into play. There are really only two ways to
know the IP address of a host: You can either know it outright, or look it
up based upon a domain name service. WinSock encapsulates the functionality
of a resolver (discussed earlier when we talked about DNS) in the
gethostbyname() function. This will look up a host-name and get the IP(s)
of the host or hosts associated with it if possible.

<P>gethostbyname() has only one parameter, namely, the name of the host to get
the address of. The address itself is returned in the form of a pointer to
a hostent structure. WinSock will allocate this for you, so all you really
need to do is get a pointer to it. Straight from the headers (well,
almost), this is what hostent looks like:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -