TCP通訊協定的server程式及client程式

我們生活周遭存在太多太多的 embedded system ,過去 embedded system 有專屬的 OS,現在 linux 也可以應用在嵌入式環境,不過本版將廣泛討論所有嵌入式系統不只 linux 喔,歡迎有這方面經驗或有興趣的朋友一同進來討論。

版主: chester

TCP通訊協定的server程式及client程式

文章shihyu » 週五 2月 09, 2007 3:54 am

動做


server程式會建立socket,繫結socket,等待client端的連線。
client程式會建立socket,與server連線,若連線成功,會將str字串傳給server,str字串的內容為【品號 品名 售價】的格式,如【A001 電視機 20000.00】
server程式若接受client端的連線,會讀取client端傳送過來的字串,並解析字串內容,算出售價的含稅價,再將處理後的字串回傳給client端。
client端收到server回傳的訊息,會將訊息顯示在螢幕上。


代碼: 選擇全部

/* server.c */

#include <stdio.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <netdb.h>

#include <string.h>

#include <stdlib.h>

int port = 8000;

int main(void)

{

  struct sockaddr_in    sin;

  struct sockaddr_in    pin;

  int           mysock;

  int           tempsock;

  int           addrsize;

  char  str[100],str1[20],str2[20],str3[20];

  char  buf[100];

  int           i, len1, len2;

  float c;

  /* 建立socket */

  mysock = socket(AF_INET, SOCK_STREAM, 0);

  if (mysock == -1) {

        perror("call to socket");

        exit(1);

  }

  /* 建立IPv4位址 */

  bzero(&sin, sizeof(sin));

  sin.sin_family = AF_INET;

  sin.sin_addr.s_addr = htonl(INADDR_ANY);

  sin.sin_port = htons(port);

  /* ?結socket */

  if (bind(mysock, (struct sockaddr *)&sin, sizeof(sin)) == -1) {

        perror("call to bind");

        exit(1);

  }

  /* 傾聽 client端 */

  if (listen(mysock, 20) == -1) {

        perror("call to listen");

        exit(1);

  }

  printf("Accepting connections ...\n");

  while(1) {

        /* 接受client端連結 */

        tempsock = accept(mysock, (struct sockaddr *)&pin, &addrsize);

        if (tempsock == -1){

                perror("call to accept");

                exit(1);

        }

 

        /* 接收client端傳來的訊息 */

        len1=recv(tempsock, str, 100, 0);

        printf("\n收到字元數: %d\n",len1);

        str[len1]=0;

        printf("received from client: %s\n", str);

        /* 分析處理client端傳來的訊息 */

        if (len1 > 0) {

                strcpy(str1,strtok(str," "));

                printf("第 1 個字串為: %s\n",str1);             

                strcpy(str2,strtok(NULL," "));

                printf("第 2 個字串為: %s\n",str2);             

                strcpy(str3,strtok(NULL," "));

                printf("第 3 個字串為: %s\n",str3);

                c=atof(str3)*1.05;

                sprintf(buf,"品號為 %s\n品名為 %s\n含稅價為: %.2f\n",str1, str2, c);           

        }

        /* 將處理過的訊息傳回給client端 */

        len2 = strlen(buf);

        if (send(tempsock, buf, len2, 0) == -1) {

                perror("call to send");

                exit(1);

        }

        /* 關閉與client端的連線 */

        close(tempsock);

  }

  return 0;

}









===============================================





代碼: 選擇全部
/* client.c */

#include <stdio.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <netdb.h>

int port = 8000;

int main(int argc, char *argv[])

{

  struct sockaddr_in    pin;

  int   mysock;

  char  buf[8192];

  char  *str="A001 電視機 20000.00 ";

  if (argc < 2) {

        printf("使用方法: client 字串\n");

        printf("使用預設字串\n");

  } else {

        str=argv[1];

  }

  /* 建立server IPv4位址 */

  bzero(&pin, sizeof(pin));

  pin.sin_family = AF_INET;

  pin.sin_addr.s_addr = inet_addr("192.168.1.20");

  pin.sin_port = htons(port);

  /* 建立socket */

  mysock = socket(AF_INET, SOCK_STREAM, 0);

  if (mysock == -1) {

        perror("call to socket");

        exit(1);

  }

  /* 連結server */

  if (connect(mysock, (void *)&pin, sizeof(pin)) == -1) {

        perror("call to connect");

        exit(1);

  }

  /* 將str字串傳給 server */

  printf("Sending message %s to server ...\n", str);

  if (send(mysock, str, strlen(str), 0) == -1) {

        perror("Error in send\n");

        exit(1);

  }

  /* 接收 server 回傳的訊息 */

  if (recv(mysock, buf, 8192, 0) == -1) {

        perror("Error in receiving\n");

        exit(1);

  }

  printf("\nResponse from server: \n\n%s\n", buf);

  /* 關閉與server的連線 */

  close(mysock);

  return 0;

}






執行結果如下

首先執行server程式,它會等待client端的連線。

[root@mylinux ex16]# gcc -o server server.c

[root@mylinux ex16]# gcc -o client client.c

[root@mylinux ex16]# ./server

Accepting connections ...



接著在另一個終端機視窗中執行client程式,它會使用預設字串,將str字串傳送給server端,並會接收server回傳的訊息。

[root@mylinux ex16]# ./client

使用方法: client 字串

使用預設字串

Sending message A001 電視機 20000.00 to server ...



Response from server:



品號為 A001

品名為 電視機

含稅價為: 21000.00



而在server端則會顯示解析client端傳來的字串。

[root@mylinux ex16]# ./server

Accepting connections ...

收到字元數: 21

received from client: A001 電視機 20000.00

第 1 個字串為: A001

第 2 個字串為: 電視機

第 3 個字串為: 20000.00

另一方面,以可以在client端輸入欲傳至server端的字串。

[root@mylinux ex16]# ./client "A002 音響 15000.00"

Sending message A002 音響 15000.00 to server ...



Response from server:



品號為 A002

品名為 音響

含稅價為: 15750.00




先編譯兩個檔案我再分別開兩個終端機執行 ./server & ./client

先執行
[root@mylinux ex16]# ./server
Accepting connections ...

再開另一個終端機
[root@mylinux ex16]# ./client



我的問題如下

不知道為什麼執行 ./client只出現下面兩行
使用方法: client 字串
使用預設字串


下面的字樣都沒出現

Sending message A001 電視機 20000.00 to server ...
Response from server:

品號為 A001
品名為 電視機
含稅價為: 21000.00


而server 那端也沒接收到訊息一直停留在
Accepting connections ...
想請問一下這是什麼原因???跟192.168.1.20 位址有關係嗎???或是port 8000??

謝謝
shihyu
懵懂的國中生
懵懂的國中生
 
文章: 184
註冊時間: 週四 11月 03, 2005 11:51 am

文章chenghao1124 » 週五 2月 09, 2007 1:13 pm

上面的程式是你自己寫的嗎?
還是改人家的程式?

如果是改人家的程式,你要把 192.168.1.20 改成你 server 的 ip 位置。
chenghao1124
可愛的小學生
可愛的小學生
 
文章: 3
註冊時間: 週五 2月 09, 2007 1:09 pm
來自: 高雄

文章shihyu » 週五 2月 09, 2007 3:36 pm

書上的範例~~ 那port 也要改嗎???
shihyu
懵懂的國中生
懵懂的國中生
 
文章: 184
註冊時間: 週四 11月 03, 2005 11:51 am

文章shihyu » 週五 2月 09, 2007 7:14 pm

剛剛改了後測試還是不行@@
shihyu
懵懂的國中生
懵懂的國中生
 
文章: 184
註冊時間: 週四 11月 03, 2005 11:51 am

文章kcw » 週五 2月 09, 2007 8:28 pm

我ok, ip改 127.0.0.1 試試
kcw
可愛的小學生
可愛的小學生
 
文章: 40
註冊時間: 週二 6月 13, 2006 11:34 pm

文章shihyu » 週五 2月 09, 2007 8:39 pm

兩台debian 主機

我測試另一台主機也是debian ... 程式可以正確執行


不過我另一台就會出現下面這樣錯誤訊息
./clinet
Error in receiving
: Connection reset by peer

再多執行幾次有時候會出現 "程式記憶體區段錯誤"字樣


./server 端出現的訊息是
call to accept: Invalid argument

想知道為什麼會這樣??? 一樣程式碼一樣是debian 為何會造成一個可以執行一個會出現錯誤訊息??

謝謝
shihyu
懵懂的國中生
懵懂的國中生
 
文章: 184
註冊時間: 週四 11月 03, 2005 11:51 am

文章chenghao1124 » 週一 2月 12, 2007 3:23 pm

你出問題那台 debian 應該是在於 server 端在 accept connection 的時候發生了錯誤,導致你 client 收到 connection reset by peer

那至於 server 端出現的 Invalid argument
我想你可能要去確定一下 accept(mysock, (struct sockaddr *)&pin, &addrsize);
裡面三個變數的正確性!
不然就是把 error 值拿出來
看是下面那種錯誤
[EBADF] The descriptor is invalid.

[EINTR] The accept() operation was interrupted.

[EMFILE] The per-process descriptor table is full.

[ENFILE] The system file table is full.

[ENOTSOCK] The descriptor references a file, not a socket.

[EINVAL] listen(2) has not been called on the socket descriptor.

[EINVAL] The addrlen argument is negative.

[EFAULT] The addr argument is not in a writable part of the user address space.

[EWOULDBLOCK] The socket is marked non-blocking and no connections are present to be accepted.

[ECONNABORTED] A connection arrived, but it was closed while waiting on the listen queue.

我個人感覺可能是 EINVAL 就是你的 addrlen 給的這個參數值有問題。

by the way, 上面的訊息是節錄至 freebsd 的 man page, 不一定會跟 debian 有點不同 :-P

port 只要不再 1024 之內,也不是你系統裡面在跑的任何一個server所用的port,就可以使用,建議可以找些 基礎網路的介紹書籍來看。
chenghao1124
可愛的小學生
可愛的小學生
 
文章: 3
註冊時間: 週五 2月 09, 2007 1:09 pm
來自: 高雄

Re: TCP通訊協定的server程式及client程式

文章drco » 週二 1月 05, 2010 4:45 pm

此範例在accept(mysock, (struct sockaddr *)&pin, &addrsize);
前加入 addrsize = sizeof(pin);
或將 int addrsize; 宣告成 socklen_t addrsize;
drco
可愛的小學生
可愛的小學生
 
文章: 5
註冊時間: 週二 1月 05, 2010 4:40 pm


回到 embedded system

誰在線上

正在瀏覽這個版面的使用者:沒有註冊會員 和 1 位訪客