📄 uclinux-server.txt
字号:
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.\n");
error = EXT_ERROR;
goto EXIT_POINT;
}
connectionMade = 1;
EXIT_POINT:
if (error != EXT_NO_ERROR) {
if (msgFd != INVALID_SOCKET) {
close(msgFd);
}
UD->msgFd = INVALID_SOCKET;
} else {
UD->msgFd = msgFd;
if(msgFd !=INVALID_SOCKET)
printf("Succeeded in creating socket!\n");
}
return(error);
}
/* Function: main
*
* Abstract:
*
*/
int main(int argc, const char *argv[])
{
int error;
const char *option=argv[1];
CD=(ConnectData *)malloc(sizeof(ConnectData));
memset(CD,0,sizeof(ConnectData));
if (strcmp(option, "-w") == 0)
CD->waitForStart=1;
else
CD->waitForStart=0;
ModeInit();
while((CD->waitForStart)&&(connectionMade==0))
{
error=OpenConnection(CD);
if(error) exit(EXIT_FAILURE);
}
init_sigaction();
init_time(2.0);
while (1);
return(EXIT_SUCCESS);
} /* end main */
下面就结合本例,介绍如何在linux(uClinux)下建立通信双方中服务器端的程序。
本例是一个服务器程序,采用流式套接字,因为流式套接字提供了一种可靠的面向连接的数据传输方法。正如它的名字所指的那样,不管是对单个的数据报,还是对于数据包,流式套接字都提供一种流式数据传输。流式套接字由socket()函数调用来创建,而且调用时必须用bind()函数为它分配一个地址。
在创建好一个套接字,并赋给它一个地址之后,需要用一种方法来建立和客户机的连接,为了做到这一点,要使用listen()函数。该函数告诉套接字开始侦听客户机的连接请求。一旦将套接字设置成侦听连接后,实际的连接就可以由accept()函数来完成。如果连接成功的接受,accept()函数将返回一个新套接字的描述符,正是由accept()函数所创建的这个新套接字会被用作以后处理新的连接。在该例程中,ConnectData结构体中的msgFd套接字就是用来真正和客户端进行通信的socket。
原来的侦听套接字将会继续侦听新的连接请求,而新的请求可能会通过accept()函数的再一次调用而获得接受。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -