socket-program.txt
来自「学习linux的工具书」· 文本 代码 · 共 1,433 行 · 第 1/3 页
TXT
1,433 行
the servsaddr points to information about the server that the client is
connecting to. The accept call creates a new socket for the server to work with
the request. This way, the server can fork() off a new process and wait for more
connections. On the server side of things, you would have code that looks like
that shown in Listing 59.1.
Listing 59.1. The server side for a socket-oriented protocol.
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <linux/net.h>
#define MY_PORT 6545
main(int argc, char *argv[])
{
int sockfd, newfd;
int cpid; /* child id */
struct sockaddr_in servaddr;
struct sockaddr_in clientInfo;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0)
{
myabort("Unable to create socket");
}
#ifdef LINUX
opt = 1; len = sizeof(opt);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,&len);
#endif
bzero((char *)&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_family = htons(MY_PORT);
/*
* The htonl (for a long integer) and htons (for short integer) convert
* a host oriented byte order * into a network order.
*/
if (bind(sockfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr)) < 0)
{
myabort("Unable to bind socket");
}
listen(sockfd,5);
for (;;)
{
/* wait here */
newfd=accept(sockfd,(struct sockaddr *)&clientInfo,
sizeof(struct sockaddr);
if (newfd < 0)
{
myabort("Unable to accept on socket");
}
if ((cpid = fork()) < 0)
{
myabort("Unable to fork on accept");
}
else if (cpid == 0) { /* child */
close(sockfd); /* no need for original */
do_your_thing(newfd);
exit(0);
}
close(newfd); /* in the parent */
}
}
In the case of connection-oriented protocols, the server performs the following
functions:
Creates a socket with a call to the socket() function
Binds itself to an address with the bind() function call
Listens for connections with the listen() function call
Accepts any incoming requests with the accept() function call
Gets incoming messages with the read() function and replies with the write()
call
Now let's look at the client side of things, in Listing 59.2.
Listing 59.2. The client side function.
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <linux/net.h>
#define MY_PORT 6545
#define MY_HOST_ADDR "204.25.13.1"
int getServerSocketId()
{
int fd, len;
struct sockaddr_in unix_addr;
/* create a Unix domain stream socket */
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
return(-1);
}
#ifdef LINUX
opt = 1; len = sizeof(opt);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,&len);
#endif
/* fill socket address structure w/our address */
memset(&unix_addr, 0, sizeof(unix_addr));
unix_addr.sin_family = AF_INET;
/* convert internet address to binary value*/
unix_addr.sin_addr.s_addr = inet_addr(MY_HOST_ADDR);
unix_addr.sin_family = htons(MY_PORT);
if (bind(fd, (struct sockaddr *) &unix_addr, len) < 0)
return(-2);
memset(&unix_addr, 0, sizeof(unix_addr));
if (connect(fd, (struct sockaddr *) &unix_addr, len) < 0)
return(-3);
return(fd);
}
The client for connection-oriented communication also takes the following steps:
Creates a socket with a call to the socket() function
Attempts to connect to the server with a connect() call
If a connection is made, requests data with the write() call, and reads
incoming replies with the read() function
Connectionless Socket Programming
Now let's consider the case of a connectionless exchange of information. The
principle on the server side is different from the connection-oriented server
side in that the server calls recvfrom() rather than the listen and accept
calls. Also, to reply to messages, the server uses the sendto() function call.
See Listing 59.3 for the server side.
Listing 59.3. The server side.
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <linux/net.h>
#define MY_PORT 6545
#define MAXM 4096
char mesg[MAXM];
main(int argc, char *argv[])
{
int sockfd, newfd;
int cpid; /* child id */
struct sockaddr_in servaddr;
struct sockaddr_in clientInfo;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0)
{
myabort("Unable to create socket");
}
#ifdef LINUX
opt = 1; len = sizeof(opt);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,&len);
#endif
bzero((char *)&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_family = htons(MY_PORT);
/*
* The htonl (for a long integer) and htons (for short integer) convert
* a host oriented byte order * into a network order.
*/
if (bind(sockfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr)) < 0)
{
myabort("Unable to bind socket");
}
for (;;)
{
/* wait here */
n = recvfrom(sockfd, mesg, MAXM, 0,
(struct sockaddr *)&clientInfo,
sizeof(struct sockaddr));
doSomethingToIt(mesg);
sendto(sockfd,mesg,n,0,
(struct sockaddr *)&clientInfo,
sizeof(struct sockaddr));
}
}
As you can see, the two function calls to process each message make this an
easier implementation than a connection-oriented one. You must, however, process
each message one at a time because messages from multiple clients can be
multiplexed together. In a connection-oriented scheme, the child process always
knows where each message originated.
The client does not have to call the connect() system call either. Instead, the
client can call the sendto() function directly. The client side is identical to
the server side, with the exception that the sendto call is made before the
recvfrom()call:
#include <sys/types.h>
#include <sys/socket.h>
int sendto((int sockfd,
const void *message__, /* the pointer to message */
int length, /* of message */
unsigned int type, /* of routing, leave 0 */
const struct sockaddr * client, /* where to send it */
int length ); /* of sockaddr */
NOTE: If you are a BSD user, use the sendto() call, not the sendmsg() call.
The sendto() call is more efficient.
Any errors are indicated by a return value of -1. Only local errors are
detected.
The recvfrom() system call is defined as shown here:
#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int sockfd,
const void *message__, /* the pointer to message */
int length, /* of message */
unsigned int flags, /* of routing, leave 0 */
const struct sockaddr * client, /* where to send it */
int length ); /* of sockaddr */
If a message is too long to fit in the supplied buffer, the extra bytes are
discarded. The call might return immediately or wait forever, depending on the
type of the flag being set. You can even set timeout values. Check the man pages
for recvfrom for more information.
There you have it--the very basics of how to program applications to take
advantage of the networking capabilities under Linux. We have not even scratched
the surface of all the intricacies of programming for networks. A good starting
point for more detailed information would be UNIX Network Programming, by W.
Richard Stevens (Prentice Hall, 1990). This book, a classic, is used in most
universities and is by far the most detailed book to date.
Record and File Locking
When two processes want to share a file, the danger exists that one process
might affect the contents of the file, and thereby affect the other process. For
this reason, most operating systems use a mutually exclusive principle: when one
process has a file open, no other process can touch it. This is called file
locking.
This technique is simple to implement. What usually happens is that a "lock
file" is created with the same name as the original file but with the extension
.lock, which tells other processes that the file is unavailable. This is how
many Linux spoolers, such as the print system and UUCP, implement file locking.
It is a brute-force method, perhaps, but effective and easy to program.
Unfortunately, this technique is not good when you must have several processes
access the same information quickly, because the delays waiting for file opening
and closing can grow to be appreciable. Also, if one process doesn't release the
file properly, other processes can hang there, waiting for access.
For this reason, record locking is sometimes implemented. With record locking, a
single part of a larger file is locked to prevent two processes from changing
its contents at the same time. Record locking enables many processes to access
the same file at the same time, each updating different records within the file,
if necessary. The programming necessary to implement record locking is more
complex than that for file locking, of course.
Normally, to implement record locking, you use a file offset, or the number of
characters from the beginning of the file. In most cases, a range of characters
is locked; the program has to note the start of the locking region and the
length of it, and then store that information where other processes can examine
it.
Writing either file-locking or record-locking code requires a good understanding
of the operating system but is otherwise not difficult, especially because
thousands of programs are readily available from the Internet, in networking
programming books, and on BBSs to examine for sample code.
Interprocess Communications
Network programming always involves two or more processes talking to each other
(interprocess communications), so the way in which processes communicate is
vitally important to network programmers. Network programming differs from the
usual method of programming in a few important aspects. A traditional program
can talk to different modules (or even other applications on the same machine)
through global variables and function calls. That doesn't work across networks.
A key goal of network programming is to ensure that processes don't interfere
with each other. Otherwise, systems can get bogged down or can lock up.
Therefore, processes must have a clean and efficient method of communicating.
UNIX is particularly strong in this regard, because many of the basic UNIX
capabilities, such as pipes and queues, are used effectively across networks.
Writing code for interprocess communications is quite difficult compared to
single application coding. If you want to write this type of routine, you should
study sample programs from a network programming book or a BBS site to see how
this task is accomplished.
Summary
Few people need to write network applications, so the details of the process are
best left to those who want them. Experience and lots of examples are the best
way to begin writing network code, and mastering the skills can take many years.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?