TCP_Socket基础编程
端口
1:网络传送数据的时,按照端口来进行数据包分类;
1):端口的取值范围在[1, 65535];
2):[1, 1023]系统保留端口;
3):[1024,5000] BSD临时端口; 用户使用
4):[5001-65535], BSD服务器(非特权)端口; 用户使用
2:哪个数据包属于哪个端口的,根据端口分类;
1):所以应用程序通过网络收发数据的时候,一定会对应一个端口;
3:查看端口占用:
1) netstat -ano 观察被占用的端口
2)ESTABLISHED状态: 表示建立了连接正在通讯;
3)CLOSE_WAIT状态: 对方已经关闭,你也要关闭你的socket;
4)TIME_WAIT: 我方主动调用close()断开连接,收到对方确认后状态变为TIME_WAIT
linux 系统下:
服务器监听socket
1: 创建一个socket,指明是TCP 的socket;
int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //int s = socket(AF_INET, SOCK_STREAM, 0);
2: bind ip地址与端口
struct sockaddr_in sockaddr; //服务端自己的IP 和 端口
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); //sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(8000); //sockaddr.sin_port = htons(port);
int ret = bind(s, (const struct sockaddr*)&sockaddr, sizeof(sockaddr));
3:监听端口:
ret = listen(s, 1); // 将socket作为监听端口;
4: 接入一个客户端:
struct sockaddr_in c_address; //客户端的IP 和 端口
int address_len = sizeof(c_address);
int client_fd = accept(s, (struct sockaddr*)&c_address, &address_len);
printf("new client comming...! %s:%d\n", inet_ntoa(c_address.sin_addr), ntohs(c_address.sin_port));
5: recv/send 数据
6: closesocket: 关闭socket,断开连接;
server.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(void)
{
struct sockaddr_in servaddr;
socklen_t cliaddr_len;
int listenfd, connfd, ret;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i, n;
//创建一个监听sock
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
goto failed;
}
//bind绑定ip地址 + 端口 监听到哪个IP和端口 指服务端
bzero(&servaddr, sizeof(servaddr)); // 给servaddr清零
servaddr.sin_family = AF_INET; //ipv4
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //"127.0.0.1"
servaddr.sin_port = htons(SERV_PORT); //"127.0.0.1" : 8000端口上
ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(ret != 0)
{
goto failed;
}
//开启监听
ret = listen(listenfd, 20);
printf("Accepting connections.....\n");
//接入一个客户端
while(1){
struct sockaddr_in cliaddr; //客户端IP地址
cliaddr_len = sizeof(cliaddr);
//connfd是我们服务端为客户端创建的配对的socket
//cliaddr 就是我们客户端的IP地址和端口
printf("waiting....!!!\n");
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
//收数据
n = read(connfd, buf, MAXLINE); //read 阻塞读取
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
for(i = 0; i < n; i++)
buf[i] = toupper(buf[i]);
printf("转换后的字符串%s\n", buf);
/*
memset(buf, 0, 80); //buf 清零
recv(connfd, buf, 80, 0);
*/
//发数据给客户端
write(connfd, buf, n);
/*
send(connfd, buf, 80, 0);
*/
//关闭
close(connfd); //closesocket(listenfd);
}
failed:
return 0;
}
服务器端
客户端连接服务器端
1: 创建一个TCP socket:
int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
2: 配置连接ip地址:
struct sockaddr_in sockaddr;
sockaddr.sin_addr.s_un.s_addr = inet_addr("127.0.0.1"); //sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(6000);
3: 连接到特定的服务器:
int ret = connect(s, ((struct sockaddr*) &sockaddr), sizeof(sockaddr));
4: recv/send 收发数据;
6: closesocket: 关闭socket;
7:客户端自己也会分配一个没有被占用的端口和服务器连接,不一定是服务器的端口;
client.c的作用是从命令行参数中获得一个字符串发给服务器,然后接收服务器返回的字符串并打印
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(int argc, char *argv[ ])
{
char buf[MAXLINE];
int sockfd, n;
char *str;
if(argc != 2)
{
fputs("usage: ./client message\n", stderr);
exit(0);
}
str = argv[1];
//1.创建一个tcp socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
//2.配置连接的IP地址 (服务端的)
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr)); //清空servaddr
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
//3.发送链接请求到我们服务端的监听socket
int ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (ret != 0) {
goto failed;
}
//4.发数据
write(sockfd, str, strlen(str));
//5.接数据
n = read(sockfd, buf, MAXLINE);
/* 另外一种非阻塞的接收发数据方式
char buf[11];
memset(buf, 0, 11);
send(s, "hallo", 5, 0);
recv(s, buf, 5, 0);
*/
printf("Response from server:\n");
printf("%s\n", buf);
//write(STDOUT_FILENO, buf, n);
close(sockfd);
failed:
return 0;
}
闭socket
1: 根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒,TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务. TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证.
2:收到关闭消息的时候,马上关闭掉对应的socket。