Linux网络编程--TCP的套接字通信学习笔记
TCP关于套接字socket通信涉及服务端和客户端两端的程序编写,如下:
A. TCP_server端;
1. 创建套接字socket
2.设置服务端ip并与socket进行绑定
3.设置服务端允许的最大连接数
3.服务器处于等待阻塞状态,等待来自客户端的连接
4.等待客户端的到来并连接成功,进行数据收发
5.关闭服务端
B. TCP_client
1.创建客户端套接字socket
2.设置需要访问的服务端ip项
3.连接服务端
4.连接成功进行数据收发
5.关闭客户端
inux网络编程是通过socket(套接字)实现的,socket是一种文件描述符。
Socket有三种类型:
流失套接字(SOCK_STREAM): 它可以提供可靠的、面向连接的通讯流;它使用TCP协议;TCP协议保证数据传输的正确性和顺序性。
数据套接字(SOCK_DGRAM):它定义了一种无连接的服务,数据通信相互独立的报文传输,是无序的,并且不保证可靠、无差错,他使用数据报文协议UDP。
原始套接字(SOCK_RAW):原始套接字使用IP协议,主要用于新网络协议的测试。
网络地址:
Socket网络程序设计中,struct sockaddr用于记录网络地址。
struct sockaddr
{
u_short sa_family; //协议簇,用于区分该网络地址属于那个协议,如IP
char sa_data[14]; //记录地址的数据
}
同理,也有用的多的另一种记录网络地址的数据结构体sockaddr_in:
Structsockaddr_in
{
short int sin_family; //同上,协议族
unsigned short int sin_port; //端口号
struct in_addr sin_addr; //协议特定地址
unsigned char sin_zero[8]; //无效位,填0
}
typedefstruct in_addr
{
union
{
struct
{
unsigned chars_b1,s_b2,s_b3,s_b4;
}S_un_b;
struct
{
unsigned short s_w1,s_w2;
}S_un_w;
unsignedlong s_addr; //32位bit记录IP
}S_un;
}IN_ADDR;
IP地址的转换:
IP地址通常有数字加点的形式(172.15.5.20)表示和用struct in_addr中使用32位的整型数据表示,这两种形式之间的转换有以下两个函数实现:
int inet_aton(const char *cp,structin_addr *inp);
char * inet_ntoa(struct in_addr in);
如上: a 表示字符串形式的ip n表示32位整型数据的IP
网络中的传输数据统一采用大端模式(big endian)传输。
字节序转换:
htons:把unsignedshort的主机序转换为网络序
htonl:把unsignedlong的主机序转换为网络序
ntohs:把unsignedshort的网络序转换为主机序
ntohl:把unsignedlong的网络序转换为主机序
IP与主机名:
在网络中标识一台主机可以用IP,也可以用主机名;例如本地主机可以用IP,也可以用主机名localhost来表示
下面这个函数就是提供主机名,获取IP:
struct hostent * gethostbyname(const char*hostname);
struct hostent
{
char * h_name; //主机的正式名字
char * h_aliases; //主机的别名
int h_addrtype; //主机的的类型
int h_length; //主机的地址长度
char **h_addr_list; //主机的IP地址列表
}
#defineh_addr h_addr_list[0]; //定义的主机的第一个IP地址
Socket编程函数:
socket--------------创建一个socket
int socket(int domain, int type, intprotocol)
domain指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族);type参数指定socket类型:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;protocol通常复制为0,该函数返回一个socket描述符,调用该函数将建立一个socket,失败返回-1,其实质分配一个socket数据结构。
bind ---------------绑定IP和端口号到socket
#include<sys/socket.h>
intbind(int socketfd, struct sockaddr *addr, socklen_t addrlen);
成功0 失败-1
socket通信时,该函数将socket与ip和端口号进行绑定
connect------------用于与服务器建立连接
listen-----设置服务器能处理的最大的连接要求
accept---用来等待来自客户端的socket连接请求
#include<sys/types.h> <sys/socket.h>
intaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回的套接字描述符,失败为-1
该函数将从连接请求队列中获得连接信息,创建新的套接字,斌返回该套接字的描述符,新的套接字用于服务器与客户端的连接通信,而旧的套接字仍处于监听状态15
send---发送数据
recv---接收数据
#include<string.h>
voidbzero((void *)s,int n); //将数据s的前n个字节置位0
下面分别是服务端和客户端的代码:
//TCP text,this is a TCP server
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#define portnumber 3333
int main()
{
struct sockaddr_in TCP_server,TCP_client;
int socketfd,newfd;
int buffer[1024],tbyte;
if((socketfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
fprintf(stderr,"Get socket error! %s \n",strerror(errno));
exit(1);
}
printf("set up the socket,and sockfd is %d\n",socketfd);
/*write the server address*/
bzero(&TCP_server,sizeof(struct sockaddr_in));
TCP_server.sin_family = AF_INET;
TCP_server.sin_port = htons(portnumber);
TCP_server.sin_addr.s_addr = htonl(INADDR_ANY);
/*bind ip to socket*/
if(bind(socketfd,(struct sockaddr *)(&TCP_server),sizeof(struct sockaddr)) == -1)
{
printf("bind ip to socket error!\n");
exit(1);
}
if(listen(socketfd,5) == -1)
{
printf("set the max connect error!\n");
exit(1);
}
while(1)
{
bzero(&TCP_client,sizeof(struct sockaddr_in));
tbyte = sizeof(struct sockaddr_in);
if((newfd = accept(socketfd,(struct sockaddr*)(&TCP_client),&tbyte)) == -1)
{
fprintf(stderr,"TCP connect failed: %s\n",strerror(errno));
exit(1);
}
printf("Get message from: %s",inet_ntoa(TCP_client.sin_addr));
if((tbyte = read(newfd,buffer,1024)) == -1)
{
printf("Get message error!\n");
exit(1);
}
buffer[tbyte] = '\0';
printf("%s\n",buffer);
memset(buffer,'\0',1024);
close(newfd);
}
return 0;
}
//TCP connect,this is a TCP client
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#define portnumber 3333
int main(int argc,char *argv[])
{
int socketfd;
struct sockaddr_in TCP_client;
struct hostent *host=NULL;
char buf[1024];
if(argc != 2)
{
printf("input error!\n");
exit(1);
}
if((host = gethostbyname(argv[1])) == NULL)
{
printf("Get name error!\n");
exit(1);
}
if((socketfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
fprintf(stderr,"Get socket error! : %s \n",strerror(errno));
exit(1);
}
/*complete TCP_client*/
bzero(&TCP_client,sizeof(struct sockaddr_in)); //clear
TCP_client.sin_family = AF_INET;
TCP_client.sin_port = htons(portnumber);
TCP_client.sin_addr = *((struct in_addr *)(host->h_addr));
if(connect(socketfd,(struct sockaddr*)(&TCP_client),sizeof(struct sockaddr)) == -1)
{
fprintf(stderr,"connect error! : %s\n",strerror(errno));
exit(1);
}
printf("connect successful!\nPlease input:\n");
fgets(buf,1024,stdin);
write(socketfd,buf,sizeof(buf));
printf("write succed!\n");
close(socketfd);
exit(0);
return 0;
}
注:学习资料参考国嵌教学视频
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】