uClinux本身就是一个网络的产物,它可以从网上供人们自由免费的下载,正是通过很多爱好者利用网络修改,改善Linux,才得到我们现在的uClinux,所以没有网络可以说就看不到今天的uClinux。因此,在学习uClinux的时候,就不能不涉及到网络,而要掌握在uClinux下设计用户应用程序,就必须要学习有关uClinux下的网络编程。本节主要讲述当前在网络编程中被广泛使用的socket。
socket一般被翻译为“套接字”,简而言之就是网络进程中的ID。
其实网络通信,本质就是进程间的通信,在网络中,每个节点都有唯一一个网络地址,即通常说的IP地址,两个进程在通信的时候,必须首先要确定通信双方的网络地址。但是网络地址只能确定进程所在的PC机,然而同一台PC可能有好几个网络进程,只有网络地址是不能够确定到底是哪个进程,所以套接字还需要提供其他信息,那就是端口号,同一台PC机,一个端口号只能分配给一个进程。所以,网络地址和端口号结合在一起,才可以共同确定整个Internet中的一个网络进程。
套接字最常用的有两种:流式套接字(Stream Socket)和数据报套接字(Datagram Socket)。在Linux中,分别称为”SOCK_STREAM”和”SOCK_DGRAM”。
这两种套接字的区别在于它们使用不同的协议。流式套接字使用TCP协议,数据报套接字使用的是UDP协议。
TCP(Transmission Control Protocol)传输控制协议,是TCP/IP体系中的运输层协议,是面向连接的,因而可提供可靠的,按序传送数据流,它的可靠是因为它使用三段握手协议来传输数据,并且采用“重发机制”确保数据的正确发送,接收端收到数据后要发出一个肯定确认,而发送端必须接收到接收端的确认信息后,否则发送端会重发数据。同时TCP是无错误传递的,有自己的检错和纠错机制,使用TCP协议的套接字是属于流式套接字。大家熟知的telnet就是使用的流式套接字。
UDP(User Datagram Protocol)用户数据报协议提供无连接的不可靠的服务,在传送数据之前不需要建立连接。远地主机在接收接收到UDP数据报后,不需要给出任何应答,这样的话,如果发送一个数据报,可能到达也可能丢失。如果发送多个包,到达接收端的次序可能是颠倒的。数据报套接字有时候也称为“无连接套接字”,大家熟悉的TFTP和NFS使用的就是该协议。
大多数情况下,如果只是将数据包发送给给定地址的机器,是不能够确定到底把数据包发送给机器哪一个进程的,端口号的指定才能够更明确的指明。适用于通信的用户应用程序可以使用从1到65535的任何一个端口号,并将它分配给端口。这些号通常分成以下几个范围段:
端口0,不使用。如果传递的端口号是0,就会为进程分配一个1024到5000之间的一个没有使用的端口。
端口1~255,保留给特定的服务,如FTP,远程网,FINGER等。
端口256~1023,保留给别的一般服务如Routing function(路由函数)。
端口1024~4999,可以被任意的客户机端口所使用,客户机套接字通常会使用这个范围段的端口。
端口5000~65535,为用户定义的服务器端口所使用。如果一个客户机需要事先知道服务器的端口,那么服务器套接字就应该使用这个范围的端口值。
下面结合一个具体的服务器端的例子,使读者熟悉socket编程的方法。
/*******************************************************
* Institute of Automation, Chinese Academy of Sciences
* File Name: comsamp.c
* Description:communication with socket
* Author: Xueyuan Nie
* Date:
*******************************************************/
#include <float.h>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
/*=========*
* Defines *
*=========*/
#ifndef TRUE
#define FALSE 0
#define TRUE 1
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXT_NO_ERROR
#define EXT_NO_ERROR 0
#endif
#ifndef EXT_ERROR
#define EXT_ERROR 1
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#ifndef SOCK_ERR
#define SOCK_ERR -1
#endif
/*==================================*
* Global data local to this module *
*==================================*/
typedef int SOCKET;
typedef struct ConnectData_tag {
int port;
int waitForStart;
SOCKET sFd; /* socket to listen/accept on */
SOCKET msgFd; /* socket to send/receive messages */
} ConnectData;
ConnectData *CD;
int i=0;
int connectionMade = 0;
/*=================*
* Local functions *
*=================*/
void prompt_info(int signumber)
{
char src[]="this is a test for socket\n";
int nBytesToSet=strlen(src);
send(CD->msgFd, src, nBytesToSet, 0);
}
void init_sigaction(void)
{
struct sigaction act;
act.sa_handler=prompt_info;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGPROF,&act,NULL);
}
void init_time(double t_usec)
{
struct itimerval value;
int int_usec;
int_usec=(int)(t_usec*1000000);
value.it_value.tv_sec=0;
value.it_value.tv_usec=int_usec; value.it_interval=value.it_value;
setitimer(ITIMER_PROF,&value,NULL);
}
int ModeInit(void)
{
int error = EXT_NO_ERROR;
error = ExtInit(CD);
if (error != EXT_NO_ERROR) goto EXIT_POINT;
printf("Succeeded in creating listening Socket by NXY\n");
EXIT_POINT:
return(error);
} /* end ModeInit */
/* Function: ExtInit
* Abstract:
* Called once at program startup to do any initialization.
* A socket is created to listen for
* connection requests from the client. EXT_NO_ERROR is returned
* on success, EXT_ERROR on failure.
* NOTES:
* This function should not block.
*/
int ExtInit(ConnectData *UD)
{
int sockStatus;
struct sockaddr_in serverAddr;
int sFdAddSize = sizeof(struct sockaddr_in);
int option = 1;
int port = 17725;
int error = EXT_NO_ERROR;
SOCKET sFd = INVALID_SOCKET;
#ifdef WIN32
WSADATA data;
if (WSAStartup((MAKEWORD(1,1)),&data)) {
fprintf(stderr,"WSAStartup() call failed.\n");
error = EXT_ERROR;
goto EXIT_POINT;
}
#endif
/*
* Create a TCP-based socket.
*/
memset((char *) &serverAddr,0,sFdAddSize);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
sFd = socket(AF_INET, SOCK_STREAM, 0);
if (sFd == INVALID_SOCKET) {
fprintf(stderr,"socket() call failed.\n");
error = EXT_ERROR;
goto EXIT_POINT;
}
/*
* Listening socket should always use the SO_REUSEADDR option
* ("Unix Network Programming - Networking APIs:Sockets and XTI",
* Volume 1, 2nd edition, by W. Richard Stevens).
*/
sockStatus = setsockopt(sFd,SOL_SOCKET,SO_REUSEADDR,(char*)&option,sizeof(option));
if (sockStatus == SOCK_ERR) {
fprintf(stderr,"setsocketopt() call failed.\n");
error = EXT_ERROR;
goto EXIT_POINT;
}
sockStatus =
bind(sFd, (struct sockaddr *) &serverAddr, sFdAddSize);
if (sockStatus == SOCK_ERR) {
fprintf(stderr,"bind() call failed.\n");
error = EXT_ERROR;
goto EXIT_POINT;
}
sockStatus = listen(sFd, 1);
if (sockStatus == SOCK_ERR) {
fprintf(stderr,"listen() call failed.\n");
error = EXT_ERROR;
goto EXIT_POINT;
}
EXIT_POINT:
UD->msgFd = INVALID_SOCKET;
UD->port=17725;
if (error == EXT_ERROR) {
if (sFd != INVALID_SOCKET) {
close(sFd);
}
UD->sFd = INVALID_SOCKET;
} else {
UD->sFd = sFd;
}
return(error);
} /* end ExtInit */
int OpenConnection(ConnectData *UD)
{
struct sockaddr_in clientAddr;
int sFdAddSize = sizeof(struct sockaddr_in);
int error = EXT_NO_ERROR;
SOCKET msgFd = INVALID_SOCKET;
const SOCKET sFd = UD->sFd;
/*
* Wait to accept a connection on the message socket.
*/
msgFd = accept(sFd, (struct sockaddr *)&clientAddr,
&sFdAddSize);
if (msgFd == INVALID_SOCKET) {
fprintf(stderr,"accept() for message socket failed.\