📄 socket.txt
字号:
/* File Name: streams.c */
#include
#include
#define TRUE 1
/* 这个程序建立一个套接字,然后开始无限循环;每当它通过循环接收到一个连接,则打印出一个信息。当连接断开,或接收到终止信息,则此连接结束,程序再接收一个新的连接。命令行的格式是:streams */
main( )
{
int sock, length;
struct sockaddr_in server;
struct sockaddr tcpaddr;
int msgsock;
char buf[1024];
int rval, len;
/* 建立套接字 */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/* 使用任意端口命名套接字 */
server.sin_family = AF_INET;
server.sin_port = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("binding stream socket");
exit(1);
}
/* 找出指定的端口号并打印出来 */
length = sizeof(server);
if (getsockname(sock, (struct sockaddr *)&server, &length) < 0) {
perror("getting socket name");
exit(1);
}
printf("socket port #%d\n", ntohs(server.sin_port));
/* 开始接收连接 */
listen(sock, 5);
len = sizeof(struct sockaddr);
do {
msgsock = accept(sock, (struct sockaddr *)&tcpaddr, (int *)&len);
if (msgsock == -1)
perror("accept");
else do{
memset(buf, 0, sizeof(buf));
if ((rval = recv(msgsock, buf, 1024)) < 0)
perror("reading stream message");
if (rval == 0)
printf("ending connection \n");
else
printf("-->%s\n", buf);
}while (rval != 0);
closesocket(msgsock);
} while (TRUE);
/* 因为这个程序已经有了一个无限循环,所以套接字"sock"从来不显式关闭。然而,当进程被杀死或正常终止时,所有套接字都将自动地被关闭。*/
exit(0);
}
客户方程序:
/* File Name: streamc.c */
#include
#include
#define DATA "half a league, half a league ..."
/* 这个程序建立套接字,然后与命令行给出的套接字连接;连接结束时,在连接上发送
一个消息,然后关闭套接字。命令行的格式是:streamc 主机名端口号
端口号要与服务器程序的端口号相同 */
main(argc, argv)
int argc;
char *argv[ ];
{
int sock;
struct sockaddr_in server;
struct hostent *hp, *gethostbyname( );
char buf[1024];
/* 建立套接字 */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/* 使用命令行中指定的名字连接套接字 */
server.sin_family = AF_INET;
hp = gethostbyname(argv[1]);
if (hp == 0) {
fprintf(stderr, "%s: unknown host \n", argv[1]);
exit(2);
}
memcpy((char*)&server.sin_addr, (char*)hp->h_addr, hp->h_length);
sever.sin_port = htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
perror("connecting stream socket");
exit(3);
}
if (send(sock, DATA, sizeof(DATA)) < 0)
perror("sending on stream socket");
closesocket(sock);
exit(0);
}
2.5 一个通用的实例程序
在上一节中,我们介绍了一个简单的socket程序实例。从这个例子我们可以看出,使用socket编程几乎有一个模式,即所有的程序几乎毫无例外地按相同的顺序调用相同的函数。因此我们可以设想,设计一个中间层,它向上提供几个简单的函数,程序只要调用这几个函数就可以实现普通情况下的数据传输,程序设计者不必太多地关心socket程序设计的细节。
本节我们将介绍一个通用的网络程序接口,它向上层提供几个简单的函数,程序设计者只要使用这几个函数就可以完成绝大多数情况下的网络数据传输。这些函数将socket编程和上层隔离开来,它使用面向连接的流式套接字,采用非阻塞的工作机制,程序只要调用这些函数查询网络消息并作出相应的响应即可。这些函数包括:
l InitSocketsStruct:初始化socket结构,获取服务端口号。客户程序使用。
l InitPassiveSock:初始化socket结构,获取服务端口号,建立主套接字。服务器程序使用。
l CloseMainSock:关闭主套接字。服务器程序使用。
l CreateConnection:建立连接。客户程序使用。
l AcceptConnection:接收连接。服务器程序使用。
l CloseConnection:关闭连接。
l QuerySocketsMsg:查询套接字消息。
l SendPacket:发送数据。
l RecvPacket:接收数据。
2.5.1 头文件
/* File Name: tcpsock.h */
/* 头文件包括socket程序经常用到的系统头文件(本例中给出的是SCO Unix下的头文件,其它版本的Unix的头文件可能略有不同),并定义了我们自己的两个数据结构及其实例变量,以及我们提供的函数说明。*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct SocketsMsg{ /* 套接字消息结构 */
int AcceptNum; /* 指示是否有外来连接等待接收 */
int ReadNum; /* 有外来数据等待读取的连接数 */
int ReadQueue[32]; /* 有外来数据等待读取的连接队列 */
int WriteNum; /* 可以发送数据的连接数 */
int WriteQueue[32]; /* 可以发送数据的连接队列 */
int ExceptNum; /* 有例外的连接数 */
int ExceptQueue[32]; /* 有例外的连接队列 */
} SocketsMsg;
typedef struct Sockets { /* 套接字结构 */
int DaemonSock; /* 主套接字 */
int SockNum; /* 数据套接字数目 */
int Sockets[64]; /* 数据套接字数组 */
fd_set readfds, writefds, exceptfds; /* 要被检测的可读、可写、例外的套接字集合 */
int Port; /* 端口号 */
} Sockets;
Sockets Mysock; /* 全局变量 */
SocketsMsg SockMsg;
int InitSocketsStruct(char * servicename) ;
int InitPassiveSock(char * servicename) ;
void CloseMainSock();
int CreateConnection(struct in_addr *sin_addr);
int AcceptConnection(struct in_addr *IPaddr);
int CloseConnection(int Sockno);
int QuerySocketsMsg();
int SendPacket(int Sockno, void *buf, int len);
int RecvPacket(int Sockno, void *buf, int size);
2.5.2 函数源文件
/* File Name: tcpsock.c */
/* 本文件给出九个函数的源代码,其中部分地方给出中文注释 */
#include "tcpsock.h"
int InitSocketsStruct(char * servicename)
/* Initialize Sockets structure. If succeed then return 1, else return error code (<0) */
/* 此函数用于只需要主动套接字的客户程序,它用来获取服务信息。服务的定义
在/etc/services文件中 */
{
struct servent *servrec;
struct sockaddr_in serv_addr;
if ((servrec = getservbyname(servicename, "tcp")) == NULL) {
return(-1);
}
bzero((char *)&Mysock, sizeof(Sockets));
Mysock.Port = servrec->s_port; /* Service Port in Network Byte Order */
return(1);
}
int InitPassiveSock(char * servicename)
/* Initialize Passive Socket. If succeed then return 1, else return error code (<0) */
/* 此函数用于需要被动套接字的服务器程序,它除了获取服务信息外,还建立
一个被动套接字。*/
{
int mainsock, flag=1;
struct servent *servrec;
struct sockaddr_in serv_addr;
if ((servrec = getservbyname(servicename, "tcp")) == NULL) {
return(-1);
}
bzero((char *)&Mysock, sizeof(Sockets));
Mysock.Port = servrec->s_port; /* Service Port in Network Byte Order */
if((mainsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return(-2);
}
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* 任意网络接口 */
serv_addr.sin_port = servrec->s_port;
if (bind(mainsock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
close(mainsock);
return(-3);
}
if (listen(mainsock, 5) == -1) { /* 将主动套接字变为被动套接字,准备好接收连接 */
close(mainsock);
return(-4);
}
/* Set this socket as a Non-blocking socket. */
if (ioctl(mainsock, FIONBIO, &flag) == -1) {
close(mainsock);
return(-5);
}
Mysock.DaemonSock = mainsock;
FD_SET(mainsock, &Mysock.readfds); /* 申明对主套接字"可读"感兴趣 */
FD_SET(mainsock, &Mysock.exceptfds); /* 申明对主套接字上例外事件感兴趣 */
return(1);
}
void CloseMainSock()
/* 关闭主套接字,并清除对它上面事件的申明。在程序结束前关闭主套接字是一个好习惯 */
{
close(Mysock.DaemonSock);
FD_CLR(Mysock.DaemonSock, &Mysock.readfds);
FD_CLR(Mysock.DaemonSock, &Mysock.exceptfds);
}
int CreateConnection(struct in_addr *sin_addr)
/* Create a Connection to remote host which IP address is in sin_addr.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -