基本概念
三次握手
主动发起连接请求端,发送SYN标志位,请求建立连接。携带数据包包号、数据字节数(0)、滑动窗口大小。
被动接收连接请求端,发送ACK标志位,同时携带SYN请求标志位。携带序号、确认序号、数据包包号、数据字节数(0)、滑动窗口大小。
主动发起连接请求端,发送ACK标志位,应答服务器连接请求。携带确认序号、数据包包号。
四次挥手
主动关闭连接请求端,发送FIN标志位。
被动关闭连接请求端,应答ACK标志位。 -------半关闭完成。
被动关闭连接请求端,发送FIN标志位。
主动关闭连接请求端,发送ACK标志位。 -------连接全部关闭
滑动窗口
发送给连接对端,本端缓冲区大小(实时),保证数据不会丢失。
出错处理封装函数
封装目的:在编程中突出逻辑,将错误处理与逻辑分开,且可以直接跳转man 手册
wacp.c
存放网络通信相关常用自定义函数
命名方式:系统调用函数首字符大写。
函数功能:调用系统调用函数,处理出错场景
联合gcc编译生成。
warp.h
存放网络通信相关常用自定义函数原型声明。
多进程并发服务器
客户端不变
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/wait.h>
#include<pthread.h>
#include<sys/socket.h>
#include<ctype.h>
#define SER_PORT 9003
void sys_err(char* s){
perror(s);
exit(1);
}
void catch_child(int signum)
{
while((waitpid(0 , NULL , 0)) > 0);
return;
}
int main(int argc , char *argv[])
{
pid_t pid;
int cfd , lfd ;
char buf[1024];
struct sockaddr_in serv_addr , clit_addr;
bzero(&serv_addr , sizeof(serv_addr));
serv_addr.sin_family = AF_INET ;
serv_addr.sin_port = htons(SER_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t clit_len;
lfd = socket(AF_INET , SOCK_STREAM , 0);
if(lfd == -1)
sys_err("socket error");
int ret = bind(lfd , (struct sockaddr*)&serv_addr , sizeof(serv_addr));
if(ret != 0)
sys_err("bind error");
listen(lfd , 128);
clit_len = sizeof(clit_addr);
while(1){
cfd = accept(lfd , (struct sockaddr*)&serv_addr , &clit_len);
if(cfd == -1)
sys_err("accept error");
pid = fork();
if(pid < 0){
sys_err("fork error");
}else if(pid > 0){
close(cfd);
struct sigaction act;
act.sa_handler = catch_child ;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD , &act , NULL);
continue;
}else{
close(lfd);
break;
}
}
if(pid == 0){
int i , n ;
while(1){
n = read(cfd , buf , sizeof(buf));
if(n == 0){
close(cfd);
exit(1);
}
for(i = 0 ; i < n ; i++){
buf[i] = toupper(buf[i]);
}
write(STDOUT_FILENO , buf ,n);
write(cfd , buf , n);}
}
return 0 ;
}
多线程并发服务器
客户端不变
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<ctype.h>
#define MAXLINE 8192
void sys_err(char*s)
{
perror(s);
exit(1);
}
void err_thread(int ret , char*str){
if(ret != 0){
fprintf(stderr,"%s:%s\n" , str , strerror(ret));
pthread_exit(NULL);
}
return ;
}
struct s_info{
struct sockaddr_in cliaddr;
int cfd;
};
void *do_work(void *arg)
{
int n,i;
struct s_info *ts = (struct s_info*)&arg;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN]; // 可用[+d查看
while(1){
n = read(ts->cfd , buf , MAXLINE);
if(n == 0){
printf("the client %d closed...\n" , ts->cfd);
break;
}
printf("received from %s at PORT %d\n" , inet_ntop(AF_INET , &(*ts).cliaddr.sin_addr , str , sizeof(str)) , ntohs((*ts).cliaddr.sin_port));
for(i = 0 ; i < n ; i++){
buf[i] = toupper(buf[i]);
}
write(STDOUT_FILENO , buf , n);
write(ts->cfd , buf , n);
}
close(ts->cfd);
return (void*) 0;
}
int main(int argc , char *argv[])
{
pthread_t tid;
struct s_info ts[256];
socklen_t client_len;
int lfd , cfd;
int ret ,i ;
struct sockaddr_in serv_addr , clit_addr;
bzero(&serv_addr , sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9002);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
lfd = socket(AF_INET , SOCK_STREAM , 0);
if(lfd == -1)
sys_err("socket error");
ret = bind(lfd , (struct sockaddr*)&serv_addr , sizeof(serv_addr));
if(ret == -1)
sys_err("bind error");
listen(lfd , 128);
while(1){
client_len = sizeof(clit_addr);
cfd = accept(lfd , (struct sockaddr*)&clit_addr , &client_len);
if(ret != 0)
sys_err("accept error");
ts[i].cliaddr = clit_addr;
ts[i].cfd = cfd;
pthread_create(&tid , NULL , do_work , (void*)&ts[i]);
pthread_detach(tid); // 子线程分离 , 防止僵尸线程产生。
i++;
}
return 0 ;
}
数返回值
1、>0 实际读到的字节数
2、=0 已经读到结尾(对端已经关闭)
3、-1 进一步判断errno的值
errno = EAGAIN or EWOULDBLOCK 设置了非阻塞方式读。没有数据到达
errno = EINTE 慢速系统调用被中断
errno = "其他情况" 异常