#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
int main()
{
int ret = 0;
int addr_len;
int portnum = 5555;//端口號
char recvbuffer[1024];//接收數據緩衝區
int serversocket,clientsocket;//服務器調用socket()之後的描述符
struct sockaddr_in server_addr;//服務器套接字
struct sockaddr_in client_addr;//客戶端套接字
/*-------------------------------結構體sockaddr_in的註釋----------------------------
頭文件:#include<netinet/in.h>
struct sockaddr_in
{
short sin_family;//Address family一般來說AF_INET(地址族)PF_INET(協議族)
unsigned short sin_port;//Port number(必須要採用網絡數據格式,普通數字可以用htons()函數轉換成網絡數據格式的數字)
struct in_addr sin_addr;//IP address in network byte order(Internet address)
unsigned char sin_zero[8];//Same size as struct sockaddr沒有實際意義,只是爲了跟SOCKADDR結構在內存中對齊;SOCKADDR結構體和該結構體意義一樣,16個字節長度。
};
-----------------------------------------------------------------------------------*/
serversocket = socket(AF_INET,SOCK_STREAM,0);
/*----------------------------------socket()註釋------------------------------------
socket()函數用於根據指定的地址族、數據類型和協議來分配一個套接口的描述字及其所用的資源。
#include <sys/socket.h>
int socket( int af, int type, int protocol);
af:一個地址描述。目前僅支持AF_INET格式,也就是說ARPA Internet地址格式。
type:指定socket類型。新套接口的類型描述類型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。
常用的socket類型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
protocol:顧名思義,就是指定協議。套接口所用的協議。如調用者不想指定,可用0。
常用的協議有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,
它們分別對應TCP傳輸協議、UDP傳輸協議、STCP傳輸協議、TIPC傳輸協議。
如果協議protocol未指定(等於0),則使用缺省的連接方式。
---------------------------------------------------------------------------------*/
if(serversocket == -1)
{
perror("socket() failed!");//頭文件#include<stdio.h>,打印上一步操作的錯誤信息
return -1; //main()函數返回-1,會導致程序退出。
//return和exit的差別就是前者是返回一個值給函數,退出該函數,而後者是屬於系統級別的,將返回值給系統,整個進程退出。
}
//初始化服務器套接字,就是將該套接字內容置0;
bzero(&server_addr,sizeof(server_addr));
/* 頭文件#include <string.h>
void bzero(void *s, int n);會將參數s所指的內存區域前n個字節,全部設爲零值。*/
//填充服務器套接字server_addr
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portnum);//頭文件:#include <arpa/inet.h>,
//必須要採用網絡數據格式-大端字節序,普通數字可以用htons()函數轉換成網絡數據格式的數字
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//設置服務器套接字的IP地址爲特殊值INADDR_ANY,這表示服務器願意接收來自任何網絡設備接口的客戶機連接。htonl()函數的意思是將主機順序的字節轉換成網絡順序的字節。
/*sin_addr類型是結構體:struct in_addr {in_addr_t s_addr;};
* 其中in_addr_t 一般爲 32位的unsigned long.
* 其中每8位代表一個IP地址位中的一個數值.
* 例如192.168.3.144記爲0xc0a80390,其中 c0 爲192 ,a8 爲 168, 03 爲 3 , 90 爲 144;
* 打印的時候可以調用inet_ntoa()函數將其轉換爲char *類型.
*/
//註釋
/*綁定套接字,使用函數:
* int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
* 其頭文件爲:#include <sys/types.h>和#include <sys/socket.h>
* */
if(bind(serversocket,(struct sockaddr *)&server_addr,sizeof(server_addr))==-1)
{
perror("bind() failed!");
return -1;
}
/*開始監聽
int listen( int sockfd, int backlog);頭文件#include <sys/socket.h>
sockfd:用於標識一個已捆綁未連接套接口的描述字。
backlog:等待連接隊列的最大長度。*/
if(listen(serversocket,10))
{
perror("listen() failed!");
return -1;
}
/*等待 客戶端連接
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//調用accept函數後,會進入阻塞狀態
//accept返回一個套接字的文件描述符,這樣服務器端便有兩個套接字的文件描述符,
//serverSocket和client_addr。
//serverSocket仍然繼續在監聽狀態,client則負責接收和發送數據
//clientAddr是一個傳出參數,accept返回時,傳出客戶端的地址和端口號
//addr_len是一個傳入-傳出參數,傳入的是調用者提供的緩衝區的clientAddr的長度,以避免緩衝區溢出。
//傳出的是客戶端地址結構體的實際長度,因此此處必須定義一個addr_len變量
//出錯返回-1*/
while(1)
{
printf("服務器等待連接請求......\n");
addr_len = sizeof(client_addr);
clientsocket = accept(serversocket, (struct sockaddr*)&client_addr, (socklen_t*)&addr_len);
if(clientsocket == -1)
{
perror("accept() failed!");
return -1;
}
printf("client IP:%s\t Port:%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
//inet_ntoa ip地址轉換函數,將網絡字節序IP轉換爲點分十進制IP
//表達式:char *inet_ntoa (struct in_addr);
while(1)
{
//接受消息,特別注意第三個參數一定要小於recvbuffer的實際長度。
bzero(recvbuffer,sizeof(recvbuffer));
ret = recv(clientsocket,recvbuffer,sizeof(recvbuffer)-1,0);
if(ret < 0 )
{
perror("recv() failed!");
continue;
}
if(ret == 0 )
{
printf("recv() is null,客戶端已經關閉,繼續監聽\n");
close(clientsocket);
break;
}
recvbuffer[ret]='\0';//如果上面recv()的第三個參數要是等於recvbuffer的長度,
//返回的ret也有可能等於這個長度,就會導致此處末尾加'\0出錯'
printf("收到的消息長度:%d\n內容:%s\n",ret,recvbuffer);
if(strcmp(recvbuffer,"quit") == 0)
{
while(1)
{
printf("客戶端:%s停止訪問!\n服務器關閉,輸入yes\t繼續監聽,輸入no\n請輸入你的選擇(yes/no):",inet_ntoa(client_addr.sin_addr));
char *qstr = (char *)malloc(10*sizeof(char));
scanf("%s",qstr);
if(strcmp(qstr,"yes") == 0)
{
printf("服務器停止監聽,關閉退出!\n");
close(clientsocket);
close(serversocket);
exit(0);
}
else if(strcmp(qstr,"no") == 0)
{
printf("服務器繼續監聽!\n");
close(clientsocket);
break;
}
else
{
printf("輸入錯誤,請重新輸入!\n");
}
}
break;
}
send(clientsocket,recvbuffer,ret,0);
}
}
return 0;
}
Linux下C語言TCP編程01
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.