一起學習CC3200系列教程之2個TCP_非阻塞及阻塞設置
阿湯哥
序:
能力有限,難免有錯,有問題請聯繫我,
QQ1519256298 [email protected]
Pdf下載http://pan.baidu.com/s/1hqiWB56
關鍵字:socket ,阻塞,非阻塞,SetSockOpt
現在我們介紹如果創建一個TCP連接。
開發環境:
CCS,sdk1.1,freertos,
軟件流程:
我們創建兩個任務,第一個任務比第二個任務多了一個網絡設置的功能,其他的一致,我們就用第一個任務來表達,任務初始化CC3200爲STA模式,並連上AP,創建一個socket,讀取遠端的數據,並把讀到的數據原封不動地發送回去。自動檢測socket連接有沒有斷開,並自動重新連接,沒有做網絡斷開連接的檢測。
這裏我們用了兩種方式:阻塞及非阻塞
阻塞:
任務會阻塞在寫和讀的函數上,直到發送出去,讀函數會直接返回,所以在返回值做了一個判斷:有沒有斷開連接,有就重連。當阻塞某一函數時,任務會自動切換。因此不用我們去延遲任務
非阻塞:
任務不會阻塞在讀的函數上,寫函數有沒有阻塞我沒測過,估計得用測量這個函數的執行時間才能知道有沒有阻塞,挺麻煩的,所以略過。當socket斷開連接會導致發送錯誤,利用這個特性,在發送錯誤時我們進行重連,因此這個會導致無用的信息傳輸浪費了電量,而且響應不及時,改進方法:用select,這個改天說。任務不會自動切換,需要我們去延遲任務,切換任務,這裏我是使用的是用SetSockOpt來設置非阻塞的
main函數
//****************************************************************************
// MAIN FUNCTION
//****************************************************************************
void main()
{
long lRetVal = -1;
//Board Initialization
BoardInit();
//
// 初始化串口
//
PinMuxConfig();
//UART Initialization
MAP_PRCMPeripheralReset(PRCM_UARTA0);
MAP_UARTConfigSetExpClk(CONSOLE,MAP_PRCMPeripheralClockGet(CONSOLE_PERIPH),
UART_BAUD_RATE,(UART_CONFIG_WLEN_8 |
UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
//Display Application Banner on UART Terminal
DisplayBanner(APPLICATION_NAME);
//
// Simplelinkspawntask
//
lRetVal = VStartSimpleLinkSpawnTask(SPAWN_TASK_PRIORITY);
if(lRetVal < 0)
{
UART_PRINT("Unable to start simpelink spawn task\n\r");
LOOP_FOREVER();
}
//創建任務1
lRetVal = osi_TaskCreate(TcpEchoClient, (signed char*)"TcpEchoClient",
TcpEchoClient_STACK_SIZE, NULL, TcpEchoClient_PRIORITY, NULL );
if(lRetVal < 0)
{
UART_PRINT("Unable to create task\n\r");
LOOP_FOREVER();
}
//創建任務2,這個任務必須是比第一個任務的優先級低,因爲第一個任務有一個初始化網絡的設置
//
lRetVal = osi_TaskCreate(TcpEchoClient2, (signed char*)"TcpEchoClient2",
TcpEchoClient_STACK_SIZE, NULL, TcpEchoClient_PRIORITY - 1, NULL );
if(lRetVal < 0)
{
UART_PRINT("Unable to create task\n\r");
LOOP_FOREVER();
}
//
// 開始任務調度
//
osi_start();
while (1)
{
}
}
任務1,2,網絡設置
//本函數是參考sdk1.1.0的network_if的函數的
static void WlanInit(void) {
long lRetVal = -1;
//
// 復位設備的wlan狀態
//
Network_IF_ResetMCUStateMachine();
//
// 把設備設置成sta模式
//
lRetVal = Network_IF_InitDriver(ROLE_STA);
if(lRetVal < 0)
{
UART_PRINT("Failed to start SimpleLink Device\n\r",lRetVal);
LOOP_FOREVER();
}
//網絡參數
SecurityParams.Key = (signed char *)SECURITY_KEY;
SecurityParams.KeyLen = strlen(SECURITY_KEY);
SecurityParams.Type = SECURITY_TYPE;
//
// 連接AP
//
lRetVal = Network_IF_ConnectAP(SSID_NAME, SecurityParams);
if(lRetVal < 0)
{
UART_PRINT("Connection to an AP failed\n\r");
LOOP_FOREVER();
}
}
void TcpEchoClient(void *pvParameters){
<span style="white-space:pre"> </span>//任務1
SlSockAddrIn_t sAddr;
SlSockNonblocking_t enableOption;
SlSockKeepalive_t xKeepaliveEnableOption;
unsigned int ulDestiantionIP;
char szRevBuf[100];
int iRevLen,iAddrSize,iStatus;
int iHeartBeatCounter = 0;
//設置網絡,
//爲什麼需要這裏設置?
//因爲本網絡設置需要啓動任務調度器:這個任務需要調度器去調度VStartSimpleLinkSpawnTask這個任務
//如果沒有啓動調度器,會導致wlan連不上。
WlanInit();
//設置服務器端的ip及端口信息
sAddr.sin_family = SL_AF_INET;
sAddr.sin_port = sl_Htons((unsigned short)9000);
//把“192.168.3.27”轉成一個沒有符號的32整數
siIp_StrToInt("192.168.3.27",&ulDestiantionIP);
sAddr.sin_addr.s_addr = sl_Htonl((unsigned int)ulDestiantionIP);
iAddrSize = sizeof(SlSockAddrIn_t);
UART_PRINT("\r\nTcpEchoClient1\r\n");
while(1) {
//g_iEchoSocketID 等於0.表示這個socket是沒有連接上的
if (g_iEchoSocketID == 0) {
//創建一個socket
g_iEchoSocketID = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, 0);
if( g_iEchoSocketID < 0 )
{
//出錯處理:略
}
// 連接到服務器
iStatus = sl_Connect(g_iEchoSocketID, ( SlSockAddr_t *)&sAddr, iAddrSize);
if( iStatus < 0 )
{
// error
sl_Close(g_iEchoSocketID);
g_iEchoSocketID = 0;
}else {
UART_PRINT("\r\n1:Echo Socket ID is %d\r\n",g_iEchoSocketID);
}
#if TCP_IS_NOBOLCK //設置成非阻塞形式,經檢測,遠端斷開連接時不會發生中斷,通知遠端斷開連接
enableOption.NonblockingEnabled = 1;
sl_SetSockOpt(g_iEchoSocketID,SL_SOL_SOCKET,SL_SO_NONBLOCKING, (_u8 *)&enableOption,sizeof(enableOption)); // Enable/disable nonblocking mode
#endif
MAP_UtilsDelay(8000000);
}
#if TCP_IS_NOBOLCK
//當設置成非阻塞的時候,遠端斷開並不會導致中斷髮生,爲了檢測遠端有沒有斷開這個連接
//需要我們去主動的發送,如果發送不成功就是斷開連接了
iHeartBeatCounter++;
if ((iHeartBeatCounter % 5) == 0){
iStatus = sl_Send(g_iEchoSocketID, "1:這是一個心跳機制的信息", sizeof("這是一個心跳機制的信息"), 0 );
if( iStatus < 0 ){
UART_PRINT("\r\n1:發送失敗 \r\n");
sl_Close(g_iEchoSocketID);
g_iEchoSocketID = 0;
}
}
#endif
//接收數據,這個接收可能是阻塞的也可能是非阻塞的,取決你的設置
iRevLen = sl_Recv(g_iEchoSocketID,szRevBuf,100,0);
if (iRevLen > 0) {
iStatus = sl_Send(g_iEchoSocketID, szRevBuf, iRevLen, 0 );
if( iStatus < 0 )
{
UART_PRINT("\r\n1:發送失敗 \r\n");
sl_Close(g_iEchoSocketID);
g_iEchoSocketID = 0;
}
} else {
UART_PRINT("\r\n1:%d 沒有收到數據 %d \r\n",g_iEchoSocketID,iRevLen);
#if TCP_IS_NOBOLCK == 0
UART_PRINT("\r\nsocket 1 斷開連接了\r\n");
g_iEchoSocketID = 0;
#endif
}
#if TCP_IS_NOBOLCK
//當是非阻塞的時候,需要主動讓任務睡眠,切換任務
osi_Sleep(1000);
#endif
}
}
void TcpEchoClient2(void *pvParameters){
SlSockAddrIn_t sAddr;
SlSockNonblocking_t enableOption;
SlSockKeepalive_t xKeepaliveEnableOption;
unsigned int ulDestiantionIP;
char szRevBuf[100];
int iRevLen,iAddrSize,iStatus;
int iHeartBeatCounter = 0;
//filling the TCP server socket address
sAddr.sin_family = SL_AF_INET;
sAddr.sin_port = sl_Htons((unsigned short)9000);
siIp_StrToInt("192.168.3.27",&ulDestiantionIP);
sAddr.sin_addr.s_addr = sl_Htonl((unsigned int)ulDestiantionIP);
iAddrSize = sizeof(SlSockAddrIn_t);
UART_PRINT("\r\nTcpEchoClient2\r\n");
while(1) {
osi_Sleep(2000);
if (g_iEchoSocketID2 == 0) {
// creating a TCP socket
g_iEchoSocketID2 = sl_Socket(SL_AF_INET,SL_SOCK_STREAM, 0);
if( g_iEchoSocketID2 < 0 )
{
}
// connecting to TCP server
iStatus = sl_Connect(g_iEchoSocketID2, ( SlSockAddr_t *)&sAddr, iAddrSize);
if( iStatus < 0 )
{
// error
sl_Close(g_iEchoSocketID2);
}else {
UART_PRINT("\r\n2:Echo Socket ID is %d\r\n",g_iEchoSocketID2);
}
#if TCP_IS_NOBOLCK //設置成非阻塞形式,經檢測,遠端斷開連接時不會發生中斷,通知遠端斷開連接
enableOption.NonblockingEnabled = 1;
sl_SetSockOpt(g_iEchoSocketID2,SL_SOL_SOCKET,SL_SO_NONBLOCKING, (_u8 *)&enableOption,sizeof(enableOption));
#endif
MAP_UtilsDelay(8000000);
}
#if TCP_IS_NOBOLCK
iHeartBeatCounter++;
if ((iHeartBeatCounter % 5) == 0){
iStatus = sl_Send(g_iEchoSocketID2, "2:這是一個心跳機制的信息", sizeof("這是一個心跳機制的信息"), 0 );
if( iStatus < 0 ){
UART_PRINT("\r\n2:發送失敗 \r\n");
sl_Close(g_iEchoSocketID2);
g_iEchoSocketID2 = 0;
}
}
#endif
iRevLen = sl_Recv(g_iEchoSocketID2,szRevBuf,100,0);
if (iRevLen > 0) {
iStatus = sl_Send(g_iEchoSocketID2, szRevBuf, iRevLen, 0 );
if( iStatus < 0 )
{
// error、
UART_PRINT("\r\n2:發送失敗 \r\n");
sl_Close(g_iEchoSocketID2);
g_iEchoSocketID2 = 0;
}
} else {
UART_PRINT("\r\n2:%d 沒有收到數據 %d \r\n",g_iEchoSocketID,iRevLen);
#if TCP_IS_NOBOLCK == 0
UART_PRINT("\r\nsocket 2 斷開連接了\r\n");
g_iEchoSocketID2 = 0;
#endif
}
#if TCP_IS_NOBOLCK
osi_Sleep(1000);
#endif
}
}
宏定義
#define TcpEchoClient_STACK_SIZE 2048
#define TcpEchoClient_PRIORITY 3
#define TCP_IS_NOBOLCK 1
/* AP Security Parameters */
SlSecParams_t SecurityParams = {0};
int g_iEchoSocketID = 0;
int g_iEchoSocketID2 = 0;
其他函數
/**
* @brief 把字符性的ip轉成整形的ip
* 譬如“192.179.1.1” -->0xc0a0101
* 返回 0 成功
* - 1 失敗,
* 請注意這個函數最好是輸入符合“192.179.1.1”這樣形式的
*/
signed int siIp_StrToInt(char *cpIp,unsigned int *uipIp) {
unsigned int retIp,temp;
unsigned char i,str[20],*p,*a[5]={0};
i = 0 ;
p = str;
strncpy(p,cpIp,16);
retIp = 0;
while((a[i] = strtok(p,".")) != NULL) {
p = NULL;
temp = strtoul(a[i],0,10);
retIp <<= 8;
retIp |= temp;
i++;
}
if (i == 4) {
*uipIp = retIp;
return 0;
} else {
return -1;
}
}