一個在linux下的基於tcp的服務器和客戶端;
具體的網絡基礎參照謝希仁老師的《計算機網絡》來入門;
在TCP/IP協議中,“IP地址+TCP或UDP端⼜號”唯⼀標識⽹
絡通訊中的⼀個進程,“IP地址+端⼜號”就稱爲socket;
先說一些規定吧:
- TCP/IP協議規定,⽹絡數據流應採⽤⼤端字節序,即低地址⾼字節。
- 先發出的數據是低地址,後發出的數據是⾼地址。
所以爲了程序的移植性,系統提供了一些接口供我們調用:
其中h表⽰host,n表⽰network,l表⽰32位長整數,s表⽰16位短整數,例 如htonl表⽰將32位的長整數從主機字節序轉換爲⽹絡字節序,例如將IP地址轉換後準備發送。
然後我們平時所見到的ip地址一般都是點分式,但是在系統中實際是一個32位的整形,所以系統也爲我們提供了一套轉換的接口:
把點分式字符串轉爲in_addr_t:
同時把in_addr_t轉回成字符串點分式的:
上篇文章中講過TCP的三次握手和四次揮手了,這次再來看其具體的通信過程及其給用戶暴露的接口:
然後我們再詳細談一下其中的接口的作用:
socket()打開⼀個⽹絡通訊端⼜,如果成功的話,就像open()⼀樣返回⼀個⽂件描述符,應⽤程序可以像讀寫⽂件⼀樣⽤read/write在⽹絡上收發數據,如果socket()調⽤出錯則返回-1。對於TCP協議,type參數指定爲SOCK_STREAM,表⽰⾯向流的傳輸協議。如果是UDP協議,則type參數指定爲SOCK_DGRAM,表⽰⾯向數據報的傳輸協 議。第三個參數可以忽略,設爲0就好了;
因爲服務器的IP地址和端口號一般都是要固定不變的,所以客戶端知道服務器的IP地址就可以訪問了,所以服務器要綁定一個固定的IP地址和端口號,所以要用的bind()函數,bind()的作⽤是將參數sockfd和myaddr綁定在⼀起,使sockfd這個⽤於⽹絡通訊的⽂件描述符監聽myaddr所描述的地址和端⼜號。
這個時候我們就要監聽是不是有客戶要來連接我們了,listen()聲明sockfd處於監聽狀態,並且最多允許有backlog個客戶端處於連接等待狀態,如果接收到更多的連接請求就忽略。listen()成功返回0,失敗返回-1。
三⽅握⼿完成後,服務器調⽤accept()接受連接,如果服務器調用accept()時還沒有客戶端的連接請求,就阻塞等待直到有客戶端連接上來。cliaddr是⼀個傳出參數,accept()返回時傳出客戶端的地址和端口號。addrlen參數是⼀個傳⼊傳出參數(value-result argument),傳⼊的是調⽤者提供的緩衝區cliaddr的長度以避免緩衝區溢出問題,傳出的是客戶端地址結構體的實際長度(有可能沒有佔滿調⽤者提供的緩衝區)。如果給cliaddr 參數傳NULL,表⽰不關⼼客戶端的地址。
這樣我們就可以的到客戶端的連接了,現在也不做多麼複雜的事情,就是把他傳來的數據讀出來,再給他寫回去就好了;
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/socket.h>
4 #incl ude <netinet/in.h>
5 #include <arpa/inet.h>
6 #include <stdlib.h>
7
8
9 int main(int argc,char* argv[])
10 {
11 if(argc != 3)//judge input
12 {
13 printf("tcp/_server + ip + port_number\n");
14 exit(1);
15 }
16 int sock = socket(AF_INET,SOCK_STREAM,0);//creat socket
17 if(sock < 0)
18 {
19 printf("creat socket error\n");
20 exit(2);
21 }
22
23 struct sockaddr_in server_socket;//init sockaddr_in
24 struct sockaddr_in client_socket;
25 server_socket.sin_family = AF_INET;
26 server_socket.sin_addr.s_addr = inet_addr(argv[1]);
27 server_socket.sin_port = htons(atoi(argv[2]));
28
29 if(bind(sock,(struct sockaddr*)&server_socket,sizeof(server_socket)) < 0)//bind sock
30 {
31 printf("bind error!\n");
32 exit(3);
33 }
34
35 if(listen(sock,5) < 0)//listen sock
36 {
37 printf("listen error\n");
38 exit(4);
39 }
40
41 while(1)
42 {
43 int len = 0;
44 int client_sock = accept(sock,(struct sockaddr*)&client_socket,&len);//accept sock
45 if(client_sock < 0)
46 {
47 printf("accept error!\n");
48 continue;
49 }
50
51 printf("a new connect from %s %d\n",inet_ntoa(client_socket.sin_addr),client_socket.sin_port);
52 while(1)
53 {//data processing
54 char buf[1024];
55 ssize_t s = read(client_sock,buf,sizeof(buf));
56 if(s>0)
57 {
58 buf[s] = 0;
59 printf("client :# %s\n",buf);
60 }
61 else
62 {
63 printf("read down ..........break;");
64 break;
65 }
66 write(client_sock,buf,sizeof(buf)-1);
67 }
68 }
69
70
71 return 0;
72 }
這樣一個基本的服務器就寫好了,只是最基本的,也只能支持一個人連接而已;
下篇再來講客戶端吧;