TCP:
首先要了解通訊的兩端:資料提供者(伺服器) 資料擷取者(用戶端) C/S模型
伺服器:(函數)
int listenfd socket(); //提供檔案描述符 socket程式設計
bind(); //将IP位址和端口号綁定到檔案描述符上 //Linux一切皆檔案,可以把檔案綁定到檔案描述符上
如果綁定失敗,有以下兩個原因:
1.IP位址不對 2.端口号被占用或是沒權限使用
listen(); //核心啟動監聽,收容用戶端連結(面向連結,不會阻塞)
int c = accept(); //擷取一個連接配接完成的用戶端 //c是伺服器連接配接上的用戶端數
服務于c:
接收資料recv();
發送資料send();
clsoe c();
close(listenfd); //檔案描述符最好手動關閉
代碼:ser.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int listenfd = -1;
void sigfun(int sign)
{
close(listenfd);
exit(0);
}
int main()
{
signal(SIGINT, sigfun);
// 協定簇 TCP協定
listenfd = socket(AF_INET, SOCK_STREAM, 0);
assert(listenfd != -1);
struct sockaddr_in ser, cli;
memset(&ser, 0, sizeof(ser));
ser.sin_family = AF_INET; // 位址簇
ser.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP位址
ser.sin_port = htons(6000);
int res = bind(listenfd, (struct sockaddr *)&ser, sizeof(ser));
assert(res != -1); // 綁定失敗: 1、IP位址不對 2、端口号被占用或者沒權限使用
listen(listenfd, 5); // size = 5 核心維護的已完成連結用戶端的檔案描述符個數(6)
// 伺服器接受不同用戶端連結的循環
while(1)
{
// 記錄用戶端的位址資訊
int clilen = sizeof(cli);
int c = accept(listenfd, (struct sockaddr*)&cli, &clilen);
assert(c != -1);
// 與一個用戶端互動的循環
while(1)
{
char buff[128] = {0};
int n = recv(c, buff, 127, 0); // 阻塞運作
if(n <= 0)
{
printf("one client unlink\n");
close(c);
break;
}
printf("%d: %s\n", c, buff);
send(c, "OK", 2, 0);
}
}
}
用戶端:(函數)
伺服器:被動連結方 用戶端:主動連結方
int socketfd = socket();
bind(); //可選 如果沒有綁定,核心也會自動選擇一個可用端口,自動探測本地的IP位址
connect(); //發起連結,與伺服器完成連結
send();
recv();
close();
代碼:cli.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
assert(sockfd != -1);
//伺服器的IP位址 端口号
struct sockaddr_in ser;
memset(&ser, 0, sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(6000); // 伺服器上對應服務程序的端口号
ser.sin_addr.s_addr = inet_addr("127.0.0.1"); // 伺服器的IP位址
int res = connect(sockfd, (struct sockaddr*)&ser, sizeof(ser));
assert(res != -1);
while(1)
{
printf("please input: ");
char buff[128] = {0};
fgets(buff, 128, stdin);
if(strncmp(buff, "end", 3) == 0)
{
close(sockfd);
break;
}
send(sockfd, buff, strlen(buff)-1, 0);
char recvbuff[128] = {0};
int n = recv(sockfd, recvbuff, 127, 0);
if(n <= 0)
{
close(sockfd);
break;
}
printf("client recv data: %s\n", recvbuff);
}
}
UDP:
伺服器:
ser.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert(sockfd != -1);
struct sockaddr_in ser,cli;
memset(&ser, 0 ,sizeof(0));
ser.sin_family = AF_INET;
ser.sin_port = htons(6500);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr *)&ser,sizeof(ser));
assert(res != -1);
while(1)
{
char buff[128] = {0};
int cli_len = sizeof(cli);
int n = recvfrom(sockfd,buff,127,0,(struct sockaddr*)&cli,&cli_len);
if(n <= -1)
{
close(sockfd);
break;
}
unsigned short cli_port = ntohs(cli.sin_port);
char *cli_ip = inet_ntoa(cli.sin_addr);
printf("%s:%u %s\n",cli_ip,cli_port, buff);
sendto(sockfd, "OK", 2,0,(struct sockaddr*)&cli,sizeof(cli));
}
}
用戶端:
cli.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert(sockfd != -1);
struct sockaddr_in ser;
memset(&ser, 0 ,sizeof(0));
ser.sin_family = AF_INET;
ser.sin_port = htons(6500);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
while(1)
{
printf("Please input:");
char buff[128] = {0};
fgets(buff, 128, stdin);
if(strncmp(buff,"end",3) == 0)
{
close(sockfd);
break;
}
sendto(sockfd,buff,strlen(buff),0, (struct sockaddr*)&ser,sizeof(ser));
char recvbuff[128] = {0};
int n = recvfrom(sockfd,recvbuff,127,0,NULL,NULL);
if(n == 0)
{
close(sockfd);
break;
}
printf("%s \n",recvbuff);
}
}
位元組流服務:
1.發送方send的次數和接收方recv的次數沒有必然聯系
2.如果發送方發一次資料,接收方一次沒有接受完,資料不會丢失
資料報服務:
1.sendto次數與recvfrom次數相同
2.如果recvfrom一次未将資料讀取完,則剩餘的會丢棄