【TCPプロトコルモジュール内の動作概要】
TCP/IPスタック内のTCPプロトコルモジュールは、TCPのコネクションの確立や
データ送受信、コネクション終了の各処理を実行する関数群から構成されてい
ます。
今回製作したTCP/IPスタックライブラリでは、TCPのサーバ機能のみとし、
かつクライアントからの指示に従う受動的な動作のみに限定しましたので
前ページの状態遷移の中の赤い太線の遷移部分のみを組み込んでいます。
特にユーザーAPI関数としてユーザーが使う関数群となっており、ユーザーが
TCP通信タスクを作成するときに使う関数です。
全体の構成は下図のようなフローとなっており、いくつかの関数の集まりとなって
います。
まず、初期化の関数Init_TCP()はTCPの全ソケットバッファのクリアを行います。
TCP_Process()がTCP受信のメイン関数で、受信したデータをソケットにコピーして
ソケットを受信処理待ちとします。
TCPisReady()関数は、ユーザーがTCPでの受信があったことを知るための関数
です。
Send_TCP()関数は、TCP通信でユーザーデータを送信するときに使う関数です。
ソケットバッファに用意したデータを送信出力します。同時にACKとPSHフラグを
Onとして送信します。
Send_ACK()関数は、TCPで受信した応答として返送データが無いときに、ACK
フラグだけを送信する関数です。
TCPで使用するユーザー用ソケットバッファの構造は、下図のようになっています。
ソケットの個数をMAX_SOCKETに設定することで増やせます
デフォルトの個数は6個です。
さらにその最大サイズもユーザーの設定が可能で、MAX_SOCKETSIZEに最大値を
バイト単位で設定します。デフォルト値は64バイトとなっています。
ここでユーザー用に開放しているソケットにはユーザーが直接書き込むことが
可能です。
ACK応答待ち用カウンタ
シーケンス番号管理用
ユーザーバッファには直接書き込み
が可能。
サイズもユーザー設定可能
【TCPプロトコル通信での使い方】
TCPプロトコルを使ってPICROS配下のアプリケーションを作る場合には、
下記のようにします。
(1) TCPのユーザータスクを作成する。
受信したTCPデータを処理し、必要であればデータを返送する
返送のための関数はあらかじめ用意されている。(Send_TCP(socket))
(2) ユーザータスクをPICROSのタスクとして追加する
タスクリンク用のヘッダファイル(tasklink.h)と、タスクディスパッチテーブル
である(tdt.h)にTCPユーザータスクを追加する。
これでTCP通信アプリケーションが出来ます。
【TCPユーザ処理タスクの作り方】
TCPプロトコルでデータを送受信することで動作するタスクは、ユーザ自身が
作成する必要があります。
このユーザー処理タスクの構成は下記のようにします。
TCPユーザーの処理は、必ずパソコン側から先に何か送信されて来て
その内容に応答する形でデバイス側から送信するものとします。
(1) ユーザー関数の定義とそれを含むユーザータスクファイルの作成
ユーザー関数を作成し、C言語のファイルとして格納します。
ファイル名、関数名は自由です。
(2) 受信データの格納されているソケットの番号を取り出す
TCPで送られて来たデータを格納している受信処理待ちソケットの番号を
TCPisReady()関数で取り出します。
☆TCP受信チェック関数のフォーマット
socket = TCPisReady()
socket:受信データのあるソケットの番号(int型)
受信データソケットが無いときは0xFFが返る
(3) ポート番号による処理分岐
ユーザーポートとして、10000から17000を使うこととしています。
そのユーザーポート番号をコマンド番号とする使い方ができます。
そのポート番号は「TCPuser[socket].TCPsokt1.PORTNO」として格納されて
いますので、それをTCPソケットから取り出してswitch文で分岐します。
☆指定ポート番号
TCPuser[socket].TCPsokt1.PORTNO に格納
(4) 受信データは TCPuser[socket].TCPsokt1.user_data[i] に格納されている
TCPでホストから送られて来たデータは配列データとして、TCPソケット内の
「user_data[i]」に格納されています。
そして格納されているデータ個数は「user_size」として格納されています。
☆ユーザーバッファのフォーマット
格納バッファ名称 TCPuser[socket].TCPsokt1.user_data[i]
データ個数 TCPuser[socket].TCPsokt1.user_size
iは変数 socketはUDPバッファ番号
(5) この受信に続いてデバイス側から送るデータが無いときはACKのみ返送
遅延ACKを使いますので、受信に引き続いてデバイス側から送るデータが
無いときには、下記関数でACKフラグのみ返送します。
☆ACK送信関数のフォーマット
Send_ACK(socket)
socket : TCPソケットの番号(int型)
(6) ホストへ送信するときには、Send_TCP(socket)関数を使う
TCPでデバイスから送信する場合には、受信データへの応答として返送する
形とします。受信処理に続いて、データを用意してTCPソケットに保存してから
下記の関数で送信を実行します。
この関数はLANライブラリに用意されているので記述するだけで大丈夫です。
このときの返送はPSHフラグと遅延ACKのACKフラグの2つをOnにして返送
されます。
☆TCP送信関数のフォーマット
Send_TCP (socket)
socket : TCPソケットの番号(int型)
(7) ソケットの使用が完了したらソケット開放
パソコン側からコネクション終了のシーケンスが始められますので、それに
従って自動的にクローズ処理が行われますので、アプリケーション側では
何もしなくても大丈夫です。
【実際のアプリ例】
では実際にアプリケーションを作成してみましょう。TCP通信のテスト用の
簡単なアプリケーションを作成してみます。
まず機能は下表のようなものとします。
《TCPテストアプリ機能》
No 機能名 使用ポート 機 能 内 容 1 データ受信
と送信10001 TCP通信で受信したデータ(文字列)を液晶表示器に表示し、
'0'から始まる32文字のASCII文字をTCP通信で返送する。2 データ受信 10002 受信したデータ(文字列)を液晶表示器に表示する。
折り返しACKのみ返信する。
3 データ受信 10003 受信したデータ(文字列)を液晶表示器に表示する。
折り返しACKのみ返信する。4 データ受信 10004 受信したデータ(文字列)を液晶表示器に表示する。
折り返しACKのみ返信する。
この機能をPICROSの1つのタスクの形で作成します。 このタスクのリストが
下記です。
(1) 最初に受信ソケット番号を求めます。
TCPisReady()関数を実行すると、受信処理待ちのソケットの番号が返されます
ので、これでソケット番号を入手します。
処理待ちが無いときは0xFFが返されますのでこのときは何もしません。
(2) このソケット中にあるポート番号を取り出して処理を分岐する。
ソケット番号が求まったら、TCPuser[socket].TCPsokt1.PORTNOでポート番号
が求められますので、このポート番号で上表のような機能に分岐します。
(3) 各機能の実行
受信したデータは、TCPuser[socket].TCPsokt1.user_data[i] にありますので
ここから1文字づつ取り出しては液晶表示器に出力します。
(4) データ送信
折り返しにデータ送信がある場合には、送信データをソケットの中の
TCPuser[socket].TCPsokt1.user_data[i]にセットし、データバイト数をやはり
TCPuser[socket].TCPsokt1.user_sizeにセットしてから、Send_TCP()関数を実行
します。 これで送信が自動的に実行されます。
(5)タスクの終了
1個のソケットの処理が終了したら 一旦EXITでそのタスクを終了させます。
☆ユーザーアプリの製作例 ファイル名 tcp_task.c
///////////////////////////////////////////////////
//// TCP user data process task
//// PORTNO : user port ID long
/// udp_data[32] : user udp data max 32 bytes
///
////////////////////////////////////////////////////
void tcp_user(void)
{
int i, socket, result;
//// 受信待ちソケット番号取り出し
socket = TCPisReady();
///// 受信待ちソケットがあるか?
if(socket != 0xFF)
{
//// 宛先ポート番号で分岐
switch (TCPsocket[socket].TCPsokt1.PORTNO )
{
case 10001:
lcd_clear();
printf(lcd_data,"%S", TCPsocket[socket].TCPsokt1.user_data);
///// ユーザーデータのセット
for (i=0; i<32; i++) //32バイトのユーザーデータ
{
TCPsocket[socket].TCPsokt1.user_data[i] = i + 0x30;
}
TCPsocket[socket].TCPsokt1.user_data[32]; //ユーザーデータ最後に0
TCPsocket[socket].TCPsokt1.user_size = 33; //ユーザーデータ数
result = Send_TCP(socket); //TCPで送信
break;
case 10002:
//// データ取り出し表示
lcd_clear();
printf(lcd_data,"%S", TCPsocket[socket].TCPsokt1.user_data);
//// ACK 返送
Send_ACK(socket);
break;
case 10003:
lcd_clear();
printf(lcd_data,"%S", TCPsocket[socket].TCPsokt1.user_data);
Send_ACK(socket);
break;
case 10004:
lcd_clear();
printf(lcd_data,"%S", TCPsocket[socket].TCPsokt1.user_data);
Send_ACK(socket);
break;
default: break;
}
Exit_Task();
}
}
【PICROSコンフィギュレーション設定】
上記ユーザーアプリのタスクを用意した上で、PICROSでLANライブラリ、
TCPユーザタスクを動作させるためには、下記のPICROSのヘッダファイルで
リンク指定をしてリンクを行います。
(1) PICROSのユーザーコンフィギュレーションでTCP/IPの使用を宣言追加
コンフィギュレーションファイル(usrconf.h)に下記部分を追加する。
これを記述すると、あとは初期化関数やアイドルループでのStackプロセス
の起動など、自動的に追加されます。
////// Define LAN Controller Use ////////////
// If define USE_STACK, then link LAN library
#define USE_STACK //スタックの使用宣言
/////// Define Task ID Name and Priority ////////
#define task_number 5
#define task1 1 //testtsk1()
#define task2 2 //testtsk2()
#define task3 3 //testtsk3()
#define task4 4 //udp_user()
#define task5 5 //tcp_user()
#define task6 6
#define task7 7
#define task8 8
////// task prototyping ////
void testtsk1(void);
void testtsk2(void);
void testtsk3(void);
void udp_user(void); //UDP関数のプロトタイプ
void tcp_user(void)
(2) TCPユーザ処理タスクをリンクする。
PICROSの「tasklink.h」にユーザ処理関数のタスクファイルをリンクします。
tsklink.hファイルの作成例は下記のようになります。
《tasklink.hの例》
/////// Task Link //////
#include <testtsk1.c>
#include <testtsk2.c>
#include <testtsk3.c>
#IFDEF USE_UDP
#include <udp_task.c>
#ENDIF
#IFDEF USE_TCP
#include <tcp\user.c> <--- これがユーザー処理関数のファイル名
#ENDIF
(3) タスクディスパッチテーブルにユーザー処理関数を追加
PICROSの「tdt.h」にユーザー処理関数名の追加をします。この関数が
上記のユーザー処理タスクファイルの中に含まれていることになります。
実際の追加例は下記のようになりますが、この記述順がタスク優先順位と
なります。
《tdt.hの例》
////////// Task Dispatch Tabel /////
dispatch(1,testtsk1())
dispatch(2,testtsk2())
dispatch(3,testtsk3())
#IFDEF USE_UDP
dispatch(4,udp_user())
#ENDIF
#IFDEF USE_TCP
dispatch(5,tcp_user()) <--- これがユーザー処理関数名
#ENDIF
【制限事項】
TCPをデータ送受信に使ってユーザー処理をするときには下記の制限が
ありますので守る必要があります。
(1) TCPのデバイス側ポート番号は 10000〜17000 を使うこと。
本来は0〜65535まで使えますが、上記範囲に制限しています。
ホスト側は特に制限していませんが、Well Known Portは使えません。
(2) 一度に送信できるデータ数は送受信とも最大制限があります。
MAX_SOCKETSIZE以上のデータを指定してもそれ以上は無視されます。
(3) 使えるPICは、PIC18シリーズに限定されています。
現状ではRAM容量制限のため、PIC18シリーズのみで動作可能と
なっています。
(4) 使用言語はCCS社C言語のPCH
PIC用のCコンパイラである、CCS社の中の PIC18用である PCH を
使用してコンパイルするものとしています。
PCHをMPLABに統合した環境にして、MPLABでコンパイル、デバッグ
作業をして下さい。
【LANライブラリソースファイル】
PICROSでTCP通信をするアプリケーション製作例のソースファイルは下記です。
MPLAB + PCHコンパイラ の環境を前提にしています。
TCPプロトコル部分をポインタを用いて記述しなおし、サイズを縮小しました。
約1kW程度小さくなり、その分高速になりました。
下記ファイルをこれまでのファイルに上書きして下さい。
★ ポインタを用いてサイズ縮小したTCPプロトコル部
ソースファイル1式は下記にあります。圧縮してあります。
★ PICROSとTCP/IPスタックライブラリででTCP通信するデモアプリ
この中には下表のソースファイルが含まれています。
ファイル名 機 能 内 容 概 要 stack.h TCPを使う設定にしたコンフィギュレーション tcp.c TCPプロトコル処理モジュール
tcp_task.c TCPのテスト用のタスク関数で、TCPの処理はここに
記述します。