关于socket:c语言实现一个简单的web服务器借助http协议

这个程序是看到的一本书上socket编程章节的课后题,题目内容很多,具体可见链接:https://www.bookstack.cn/read...实现一个简略的Web服务器myhttpd。服务器程序启动时要读取配置文件/etc/myhttpd.conf,其中须要指定服务器监听的端口号和服务目录,我设置的是如下:Port=8000Directory=/var/www在Directory即服务器的/var/www放入你想要加载到client(此处是浏览器)的文件,如图片或者是html文件。能够看到这是我的ecs外面放的要加载的文件。 多过程传输的server的代码如下所示,留神是在linux环境下编程和执行! #include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <sys/wait.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netinet/in.h>#include <fcntl.h>#define RECV_PORT 8000#define QLEN 10#define MAX_SIZE 1024char browser_com_buf[256]; int mylisten (void){ int listenfd; int err; socklen_t addrlen; struct sockaddr_in server_addr; char *address = "101.132.101.121"; listenfd = socket ( AF_INET, SOCK_STREAM, 0 ); /*creat a new socket*/ if ( listenfd < 0 ) { printf ( "listen error\n" ); return -1; } memset ( &server_addr, 0, sizeof ( server_addr ) ); server_addr.sin_family = AF_INET; /*IPV4*/ server_addr.sin_port = htons ( RECV_PORT ); server_addr.sin_addr.s_addr = htonl ( INADDR_ANY ); addrlen = sizeof ( server_addr ); err = bind ( listenfd, ( struct sockaddr * )&server_addr, addrlen ); if ( err < 0 ) { printf ( "bind error\n" ); return -2; } err = listen ( listenfd, QLEN ); if ( err < 0 ) { printf ( "listen error" ); return -3; } return listenfd;}int myaccept ( int listenfd ){ int clientfd; int err; socklen_t addrlen; struct sockaddr_in client_addr; addrlen = sizeof ( client_addr ); clientfd = accept ( listenfd, ( struct sockaddr * )&client_addr, &addrlen ); if ( clientfd < 0 ) { printf ( "accept error\n" ); return -1; } return clientfd;}void browser_com_analysis ( char *com, char *buf ){ int i = 0; unsigned int flag = 0; char *locate; locate = strchr ( com, '/' ); //顺次检索com字符串中的每一个字符,直到遇见字符 /,返回第一次呈现字符/的地位 if ( locate == NULL ) { printf ( "not find\n" ); exit ( 1 ); } else { //把命令逐字符存到buf中 while ( *locate != ' ' ) { buf[i++] = *locate; locate++; } buf[i] = '\0'; } //printf ( "%s\n", buf );}//判断文件的权限和状态,是纯txt/html还是图片,还是CGI程序(可执行文件)int state_estimat ( char *buf ){ int len; unsigned int flag = 0; int i = 0, j = 0; char buf1[256]; char *image_style = ".jpg"; char file_name[256] = { "/var/www" }; struct stat statbuf; len = strlen ( buf ); memset ( buf1, '\0', sizeof ( buf1 ) ); while ( i < len ) { //将文件格式后缀的内容存到buf[1],如test.jpg将.jpg存到buf1中 if ( buf[i] == '.' ) { flag = 1; } if ( flag == 1 ) { if ( buf[i] == ' ' || buf[i] == '\n' ) { break; } else { buf1[j++] = buf[i]; } } i++; } //printf ( "%s%d\n", buf1, len ); if ( len == 0 ) { printf ( "http have not send comand\n" ); exit ( 1 ); } //GET 之后只有一个命令/,所以len == 1,在本例中/实际上是/var/www //Web服务器应该把该目录下的索引页(默认是index.html)发给浏览器. //也就是把/var/www/index.html发给浏览器 else if ( len == 1 ) { return 1; } else if ( len > 1 ) { //将参数 src 字符串复制到参数 dest 所指的字符串尾部;dest 最初的完结字符 NULL 会被笼罩掉. //并在连贯后的字符串的尾部再减少一个 NULL。 strcat ( file_name, buf ); stat ( file_name, &statbuf ); //stat()用来将参数file_name 所指的文件状态, 复制到参数statbuf所指的构造中 if ( statbuf.st_mode & S_IXOTH ) //其余用户具备可执行权限 { //如果具备可执行权限,则执行该文件,将后果发送到浏览器上 return 4; } else { //不具备可执行权限则判断图片还是文件 if ( strcmp ( buf1, image_style ) == 0 ) { return 2; //浏览器申请的是图片 } return 3; //浏览器申请的是文件 } }}int find_file ( char *buf, int clientfd ){ int state; char my_file_buf[65536 * 2]; int n; char fault[] = { "HTTP/1.1 404 Not Found\nContent-Type: text/html\n<html><body>request file not found</body></html>\n\n" }; char head[] = { "HTTP/1.1 200 OK\nContent-Type: text/html\n\n" }; char head2[] = { "HTTP/1.1 200 OK\nContent-Type: image/jpg\n\n" }; char head3[] = { "HTTP/1.1 200 OK\n\n" }; char file_name[] = { "/var/www" }; memset ( &my_file_buf, '\0', MAX_SIZE ); state = state_estimat ( buf ); //Web服务器应该把该目录下的索引页(默认是index.html) if ( state == 1 ) //server send file to browser { int filefd; filefd = open ( "/var/www/index.html", O_RDWR );//返回0示意胜利 //参数 pathname 指向欲关上的文件门路/var/www/index.html. if ( filefd < 0 ) { printf ( "find file failed\n" ); write ( clientfd, fault, strlen ( fault ) ); close ( clientfd ); } else { n = read ( filefd, my_file_buf, MAX_SIZE );//胜利返回读到的字节数 //read()会把参数fd 所指的文件传送count个字节到buf 指针所指的内存中 if ( n < 0 ) { printf ( "read the root file failed\n" ); exit ( 1 ); } strcat ( head, my_file_buf );//将从文件获取到的内容放到head头内容 的上面 printf ( "%s", head ); write ( clientfd, head, strlen ( head ) );//胜利返回理论写入的字节数 //把参数buf 所指的内存写入count 个字节到参数fd 所指的文件内 close ( clientfd ); } } else if ( state == 2 ) { printf ( "picture\n" ); int picture_fd; int n; write ( clientfd, head2, strlen ( head2 ) ); memset ( &my_file_buf, '\0', sizeof ( my_file_buf ) ); strcat ( file_name, buf ); picture_fd = open ( file_name, O_RDONLY ); //只读关上图片文件 if ( picture_fd < 0 ) { printf ( "open picture failed\n" ); exit ( 1 ); } n = read ( picture_fd, my_file_buf, sizeof ( my_file_buf ) ); //将fd所指的文件传送count个字节到my_file_buf,此处文件是图片,所以传的是二进制码 if ( n < 0 ) { printf ( "read picture data failed\n" ); exit ( 1 ); } write ( clientfd, my_file_buf, sizeof ( my_file_buf ) ); close ( clientfd ); } else if ( state == 3 ) { //3是指一般不可执行文件 printf ( "file\n" ); int file_fd; int n; write ( clientfd, head, strlen ( head ) );//head是“text/html” memset ( &my_file_buf, '\0', sizeof ( my_file_buf ) ); strcat ( file_name, buf );//buf是指浏览器申请的文件名放在file_name /var/www之后 file_fd = open ( file_name, O_RDONLY ); if ( file_fd < 0 ) { printf ( "open unCGI-file failed\n" ); exit ( 1 ); } n = read ( file_fd, my_file_buf, sizeof ( my_file_buf ) ); if ( n < 0 ) { printf ( "read unCGI-file failed \n" ); exit ( 1 ); } write ( clientfd, my_file_buf, sizeof ( my_file_buf ) ); } else if ( state == 4 ) { //是可执行文件 printf ( "executable file\n" ); pid_t pid; pid = fork (); if ( pid < 0 ) { printf ( "creat child failed\n" ); exit ( 1 ); } else if ( pid > 0 ) //parent { int stateval; waitpid ( pid, &stateval, 0 ); //waitpid()会临时进行目前过程的执行, wait有信号来到或子过程完结而后彻底清除该子过程 //如果在调用wait()时子过程曾经完结, 则wait()会立刻返回子过程完结状态值. //子过程的完结状态值会由参数status返回 //wait期待第一个终止的子过程,而waitpid能够通过pid参数指定期待哪一个子过程 close ( clientfd ); } else //child用来执行该可执行文件并返回后果到浏览器输入 { int err; char *argv[1];//char* argv[1]指针数组,只有一个指针元素 strcat ( file_name, buf ); argv[1] = file_name; dup2 ( STDOUT_FILENO, clientfd ); //用dup2重定向子过程的规范输入到客户端socket err = execv ( "/bin/bash", argv );//执行CGI 如/bin/bash test.sh //其规范输入会向dup2指定的那样定向到浏览器 //不带字母p(示意path)的exec函数第一个参数必须是程序的相对路径或绝对路径 if ( err < 0 ) { printf ( "executable file failed\n" ); exit ( 1 ); } close ( clientfd ); } }}int main ( int argc, char *argv[] ){ int listenfd, clientfd; int filefd, n; char recvbuf[MAX_SIZE]; char data_buf[MAX_SIZE]; listenfd = mylisten(); if ( listenfd < 0 ) { exit ( 0 ); } printf ( "listening and listen fd is %d......\n", listenfd ); while ( 1 ) { clientfd = myaccept ( listenfd ); printf ( "accept success......\n" ); if ( clientfd < 0 ) { exit ( 1 ); } memset ( &recvbuf, '\0', MAX_SIZE ); memset ( &data_buf, '\0', MAX_SIZE ); n = read ( clientfd, recvbuf, MAX_SIZE ); printf ( "read OK\n" ); if ( n < 0 ) { printf ( "read error\n" ); exit ( 2 ); } filefd = open ( "/tmp/read_html.txt", O_RDWR | O_CREAT | O_TRUNC ); //open o new file write ( filefd, recvbuf, n ); //save the http information to filefd browser_com_analysis ( recvbuf, data_buf );//将收到的命令解析出/+前面有用的保留在data_buf find_file ( data_buf, clientfd );//对浏览器的申请内容进行判断,是一般html还是picture或是CGI } close ( clientfd ); return 0;}让咱们来看一下成果:1、首先在linux中运行该程序,此处我应用的vscode近程连贯linux,能够间接运行server,如果你是在centos默认的界面或者vim编辑,能够应用gcc http_sever.c -o http_sever生成可执行文件 和 ./http_sever来执行该文件,能够看到服务器开始listen监听是否有连贯申请: ...

April 9, 2022 · 6 min · jiezi

关于socket:windows本地SOCKET通信

业务场景是主控机A和数据卡B通过UDP通信。因为软件测试,迫于环境须要本地过程间通信。间接将原先跑在两台设施的软件跑在同一台机器,置单方IP都为127.0.0.1。原先主控机和数据卡的接管端口都为6002,现同一台设施运行,两个过程端口不能够雷同。将主控机的接管端口改为6003,即可失常通信。代码如下 UDP初始化库/*-----------------------------------------------------------**** 文件名: UDP_Driver.c**** 形容: 定义了通过UDP进行通信所需的所有函数和变量**** 定义的函数:** UDP_Init()** UDP_Send()** UDP_Recv()**** 设计注记:********-----------------------------------------------------------*//*UDP运行环境*/#ifdef _WIN32#define UDP_UNDER_VXWORKS 0#else#define UDP_UNDER_VXWORKS 2 /*Windows C++运行环境下定义为0, vxWorks5.x环境下定义为1, vxWorks6.x环境下定义为2*/#endif#if UDP_UNDER_VXWORKS > 0//#include <sysIO.h>#include <netinet/in.h>#include <sockLib.h>#include <string.h>#include <stdio.h>#include <fcntl.h>#include "errnoLib.h"#include "UDP_Driver.h"#if UDP_UNDER_VXWORKS == 2#ifndef SOCKADDR //typedef struct sockaddr SOCKADD#endif#endiftypedef unsigned int SOCKET;#else#include <WinSock2.h>#include <stdio.h>#pragma comment(lib, "WS2_32.lib")#include "UDP_Driver.h"typedef char* caddr_t;static WSADATA wsaData;#endiftypedef struct sockaddr_in SockAddr_TYPE;typedef struct{ int (*Send)(void * const Handler, void * const Payload, int const LenBytes); int (*Recv)(void * const Handler, void * const Buffer, int const MaxBytes); SOCKET Recv_Socket;/*接管和发送的socket*/ SockAddr_TYPE Self_Recv_Address;/*配置接管IP与端口号,用于创立Recv_Socket*/ SockAddr_TYPE Peer_Send_Address;/*配置源端IP与端口号,用于断定接收数据起源*/ SockAddr_TYPE Peer_Recv_Address;/*配置目标IP与端口号,用于指定发送地址*/} UDP_INNER_TYPE;/*.BH-------------------------------------------------------------**** 函数名: UDP_Send**** 形容: UDP发送函数**** 输出参数:** Handler : void * const, UDP句柄** Payload : void * const, 发送数据指针** LenBytes: int const, 发送数据长度**** 输入参数:** 返回值: int, > 0, 发送胜利(返回发送数据长度, 单位:BYTE)** -1, 句柄有效** -2, Payload为空或LenBytes小于1** -3, Socket故障**** 设计注记:****.EH-------------------------------------------------------------*/int UDP_Send(void * const Handler, void * const Payload, int const LenBytes){ UDP_INNER_TYPE * pUDP = (UDP_INNER_TYPE*)Handler; int ret_val = 0; if (pUDP == NULL) ret_val = -1; else if ((Payload == NULL) || (LenBytes < 1)) ret_val = -2; else { ret_val = sendto(pUDP->Recv_Socket/*pUDP->Send_Socket*/, (caddr_t)Payload, LenBytes, 0, (SOCKADDR*)&pUDP->Peer_Recv_Address, sizeof(SockAddr_TYPE));#if UDP_UNDER_VXWORKS > 0 if (ret_val == ERROR)#else if (ret_val == SOCKET_ERROR)#endif { //printf(" UDP_Send Error %s\n", strerror(errno)); //printf("UDP_Send errno = %d\n", WSAGetLastError()); ret_val = -3; } } return ret_val;}/* END of UDP_Send *//*.BH-------------------------------------------------------------**** 函数名: UDP_Recv**** 形容: UDP接管函数**** 输出参数:** Handler : void * const, UDP句柄** Buffer : void * const, 寄存接收数据的缓存指针** MaxBytes: int const, Buffer的最大容量**** 输入参数:** 返回值: int, >= 1, 理论获取到的数据长度(单位:BYTE)** 0, 无数据可接管** -1, 句柄有效** -2, Buffer为空或MaxBytes长度有余** -3, Socket故障**** 设计注记:****.EH-------------------------------------------------------------*/int UDP_Recv(void * const Handler, void * const Buffer, int const MaxBytes){ UDP_INNER_TYPE * pUDP = (UDP_INNER_TYPE*)Handler; SockAddr_TYPE Source_Address; int Source_AddrSize = sizeof(SockAddr_TYPE); int ret_val = 0; if (pUDP == NULL) ret_val = -1; else if ((Buffer == NULL) || (MaxBytes < 1)) ret_val = -2; else { while ((ret_val = recvfrom(pUDP->Recv_Socket, (char*)Buffer, MaxBytes, 0, (SOCKADDR*)&Source_Address, &Source_AddrSize)) > 0) { if ((0xC00A0050 == pUDP->Peer_Send_Address.sin_addr.s_addr) /*&& (Source_Address.sin_port == pUDP->Peer_Send_Address.sin_port)*/) { // just for test()MMP Send Data, data monitor in wireshark, but IDMP have no data recived! } /*对端往用XXXX发数据,本端就往对端的这个端口XXXX发数据(默认认为对端收发接口统一)*/ if ((Source_Address.sin_addr.s_addr == pUDP->Peer_Send_Address.sin_addr.s_addr) /*&& (Source_Address.sin_port == pUDP->Peer_Send_Address.sin_port)*/) { //printf("[UDP_Recv] recv port %d, %d\n", htons(Source_Address.sin_port), htons(pUDP->Self_Recv_Address.sin_port)); pUDP->Peer_Recv_Address.sin_port = Source_Address.sin_port; break; } }#if 0 ret_val = recvfrom(pUDP->Recv_Socket, (char*)Buffer, MaxBytes, 0, (SOCKADDR*)&Source_Address, &Source_AddrSize); if(ret_val > 0){ if ((Source_Address.sin_addr.s_addr == pUDP->Peer_Send_Address.sin_addr.s_addr) /*&& (Source_Address.sin_port == pUDP->Peer_Send_Address.sin_port)*/) { pUDP->Peer_Recv_Address.sin_port = Source_Address.sin_port; } }#endif if (ret_val < 0){ ret_val = -3; } } return ret_val;}/* END of UDP_Recv *//*.BH-------------------------------------------------------------** ** 函数名: UDP_Init**** 形容: UDP初始化接口函数**** 输出参数:** Handler : UDP_STRUCT_TYPE * const, 需初始化的UDP句柄** Peer_IP : unsigned int const, 对方设施IP地址** Peer_Send_Port : unsigned short const, 对方设施发送UDP端口号** Peer_Recv_Port : unsigned short const, 对方设施接管UDP端口号** Self_IP : unsigned int const, 本方设施IP地址(单网卡环境可置零,多网卡环境必须指定通信网卡地址)** Self_Send_Port : unsigned short const, 本方设施发送UDP端口号** Self_Recv_Port : unsigned short const, 本方设施接管UDP端口号** Recv_Cache : int const, 接管缓存大小(零碎默认为8192, 大数据量时可减少)** Recv_Block_Mode: int const, 接管模式: 0-阻塞, 1-非阻塞**** 输入参数:** 返回值: int, >= 0, 初始化胜利** -1, 初始化失败(句柄有效)**** 设计注记:****.EH-------------------------------------------------------------*/int UDP_Init(UDP_STRUCT_TYPE * const Handler, unsigned int const Peer_IP, unsigned short const Peer_Send_Port, unsigned short const Peer_Recv_Port, unsigned int const Self_IP, unsigned short const Self_Send_Port, unsigned short const Self_Recv_Port, int const Recv_Cache, int const Recv_Block_Mode){ UDP_INNER_TYPE *pUDP = (UDP_INNER_TYPE*)Handler; unsigned int Recv_Mode; int Ret;#if UDP_UNDER_VXWORKS == 0 static int First_Init = 1; if (First_Init) WSAStartup(MAKEWORD(2, 2), &wsaData); First_Init = 0;#endif if (pUDP == NULL) return -1; /*send buffer默认100000*/ // if(0 == getsockopt(pUDP->Send_Socket, SOL_SOCKET, SO_SNDBUF, buffer, &Send_Cache)) // printf("udp send buff size %d\n", *(int*)buffer); pUDP->Self_Recv_Address.sin_family = AF_INET; pUDP->Self_Recv_Address.sin_addr.s_addr = htonl(Self_IP); pUDP->Self_Recv_Address.sin_port = htons(Self_Recv_Port); pUDP->Recv_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); Ret = bind(pUDP->Recv_Socket, (SOCKADDR*)&(pUDP->Self_Recv_Address), sizeof(SockAddr_TYPE)); if (Ret < 0) { printf("Bind Failed, Ret-%d, sock-%d\n", Ret, pUDP->Recv_Socket); return -2; } if (Recv_Cache > 8192){ setsockopt(pUDP->Recv_Socket, SOL_SOCKET, SO_RCVBUF, (char*)&Recv_Cache, sizeof(int)); setsockopt(pUDP->Recv_Socket, SOL_SOCKET, SO_SNDBUF, (char*)&Recv_Cache, sizeof(int)); } Recv_Mode = (Recv_Block_Mode > 0);#if UDP_UNDER_VXWORKS > 0 ioctl(pUDP->Recv_Socket, FIONBIO, &Recv_Mode);#else ioctlsocket(pUDP->Recv_Socket, FIONBIO, (u_long*)&Recv_Mode);#endif pUDP->Peer_Send_Address.sin_family = AF_INET; pUDP->Peer_Send_Address.sin_addr.s_addr = htonl(Peer_IP); pUDP->Peer_Send_Address.sin_port = htons(Peer_Send_Port); pUDP->Peer_Recv_Address.sin_family = AF_INET; pUDP->Peer_Recv_Address.sin_addr.s_addr = htonl(Peer_IP); pUDP->Peer_Recv_Address.sin_port = htons(Peer_Recv_Port); pUDP->Send = UDP_Send; pUDP->Recv = UDP_Recv; return 0;}/* END of UDP_Init */#undef SockAddr_TYPE#if UDP_UNDER_VXWORKS > 0 #undef SOCKADDR #undef SOCKET#else #undef caddr_t#endif#undef UDP_UNDER_VXWORKS主控机初始化(A):#define DLR_CARD_IP 0x7F000001/*0xAC100A0F*/#define DLR_RECV_IDMP_PORT 6002#define IDMP_RECV_DLR_PORT 6003/*6002*//*BASEIO_UDP0_CHANNEL_DLR:服务器端(DLR)IP地址:172.16.10.15*/Ret = UDP_Init(&UDP_Handler[0], DLR_CARD_IP, DLR_RECV_IDMP_PORT, DLR_RECV_IDMP_PORT, IDMP_SIM_IP1, 0, IDMP_RECV_DLR_PORT, 64 * 1024, UDP_RECV_NONBLK);if (Ret != 0) { IOM_Report_Fault(Chn++, CDMP_FAIL); USR_LOG(LOGER_IOSS, LOG4C_PRIORITY_ERROR, "%s, %d UDP_Init Error %d\n", "DLR", 0, Ret);}else { IOM_Report_Fault(Chn++, CDMP_PASS);}数据卡端初始化(B):#define UdpLocalIP 0x7F000001#define DLR_RECV_IDMP_PORT 6002#define IDMP_RECV_DLR_PORT 6003/*6002*/Ret = UDP_Init(&UDP_Handler[BASEIO_UDP0_CHANNEL_DLR], UdpLocalIP/*0xAC100A10*/, IDMP_RECV_DLR_PORT, IDMP_RECV_DLR_PORT, UdpLocalIP, DLR_RECV_IDMP_PORT, DLR_RECV_IDMP_PORT, 64 * 1024, UDP_RECV_NONBLK);if (Ret != 0) { //IOM_Report_Fault(Chn++, CDMP_FAIL); USR_LOG(LOGER_IOSS, LOG4C_PRIORITY_ERROR, "MCU UDP_Init Error %d\n", Ret);}这样,一个点对点的本地过程间UDP通信模型就建设实现了。不论是A发B还是B发A,都能够失常通信。如果谋求效率能够将SOCKET绑定为AF_UNIX,通过文件进行过程间数据收发。 ...

January 6, 2022 · 4 min · jiezi

关于socket:详解什么是-socket套接字插座

 欢送大家搜寻“小猴子的技术笔记”关注我的公众号,有问题能够及时和我交换。 你晓得插座吗?你晓得网络编程中的插座吗?兴许你会有点蛊惑,什么是插座!然而我如果说出“套接字”、“socket”这样的关键字你就会豁然开朗。 所谓的“插座”叫做套接字又叫做socket,用来示意一个端点,能够与网络中其余的socket进行连贯,而后进行数据的传输。 咱们都晓得在网络上中能够通过IP地址确定惟一的一台主机,而后主机和主机之间进行通信。然而精确来说:网络通讯中的单方并不是主机,而是主机中的过程。这就须要确定主机中那个过程进行的网络通讯,因而还须要一个端口号来确定主机中的惟一过程。 如果从操作系统的角度来说:套接字是应用规范的Unix文件描述符来与其余计算机进行通信的一种形式。 IP+PORT的组合就形成了网络中惟一标识符“套接字”。端口号的范畴是0-65535,然而低于256的端口号进行了保留,作为零碎的规范的应用程序端口。比方HTTP的默认的80,POP3的110,Telent的23,FTP的21等。 套接字容许两个过程进行通信,这两个过程可能运行在同一个机器上,也可能运行在不同机器上。 套接字次要有两类:流式套接字和数据报套接字。 流式套接字提供了面向连贯、牢靠的数据传输服务,能够十分精确的实现依照程序接收数据。如果你通过流式套接字发送"h","e","l","l","o"五个字符,它达到另一端的程序也将会是"h","e","l","l","o"。起因就在于流式套接字应用的是TCP进行数据传输,可能保证数据的安全性。流式套接字是最常应用的,一些家喻户晓的协定应用的都是它,如HTTP,TCP,SMTP,POP3等。 数据报套接字:提供无连贯的服务,你不须要像流式套接字那样建设一个连贯,而只须要将地址信息一起打包而后收回去。该服务应用的是UDP进行传输,提早小,效率高然而不能保障数据传输的准确性。 如果咱们创立一个ServerSocket须要经验以下几个步骤:bind-->listen-->accpet-->recv-->write-->close。这也是底层Linux/或者Unix对一个端口监听经验的步骤。 bind:绑定一个地址和端口,确认端点的信息。 ServerSocket serverSocket = new ServerSocket(8888); 这里兴许你看到了并没有指明IP,那么“ServerSocket”会主动获取本机的ip地址作为填充,源码如下: public ServerSocket(int port) throws IOException { this(port, 50, null);}public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException { setImpl(); if (port < 0 || port > 0xFFFF) throw new IllegalArgumentException("Port value out of range: " + port); if (backlog < 1) backlog = 50; try { bind(new InetSocketAddress(bindAddr, port), backlog); } catch(SecurityException e) { close(); throw e; } catch(IOException e) { close(); throw e; }}public InetSocketAddress(InetAddress addr, int port) { holder = new InetSocketAddressHolder( null, addr == null ? InetAddress.anyLocalAddress() : addr, checkPort(port));} 会对“InetAddress”进行判断,如果为空了就获取本地的地址。 ...

February 19, 2021 · 1 min · jiezi

关于socket:网络与socket

https://blog.csdn.net/zhihuiy...这篇文章写的也不错,socket={ip,port}临时不了解过程为什么要用ip+协定+port作为惟一标识 目录 网络IP地址端口socket总结1 网络1.1 网络定义网络就是一种辅助单方或者多方可能连贯在一起的工具如果没有网络可想单机的世界是如许的孤独1.2 应用网络的目标就是为了联通多方而后进行通信用的,即把数据从一方传递给另外一方如果没有网络,编写的程序都是单机的,即不能和其余电脑上的程序进行通信为了让在不同的电脑上运行的软件,之间可能相互传递数据,就须要借助网络的性能1.3 总结应用网络可能把多方链接在一起,而后能够进行数据传递所谓的网络编程就是,让在不同的电脑上的软件可能进行数据传递,即过程之间的通信2 IP地址2.1 ip地址的作用ip地址:用来在网络中标记一台电脑,比方192.168.1.1;在本地局域网上是惟一的。 2.2 ip地址的分类每一个IP地址包含两局部:网络地址和主机地址 2.2.1 A类IP地址一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”地址范畴1.0.0.1-126.255.255.254二进制示意为: 00000001 00000000 00000000 00000001 - 01111110 11111111 11111111 11111110 可用的A类网络有126个,每个网络能包容1677214个主机 2.2.2 B类IP地址一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”地址范畴128.1.0.1-191.255.255.254二进制示意为: 10000000 00000001 00000000 00000001 - 10111111 11111111 11111111 11111110 可用的B类网络有16384个,每个网络能包容65534主机 2.2.3 C类IP地址一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”范畴192.0.1.1-223.255.255.254二进制示意为: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110 C类网络可达2097152个,每个网络能包容254个主机 2.2.4 D类地址用于多点播送D类IP地址第一个字节以“1110”开始,它是一个专门保留的地址。它并不指向特定的网络,目前这一类地址被用在多点播送(Multicast)中多点播送地址用来一次寻址一组计算机 s 地址范畴 224.0.0.1-239.255.255.254 2.2.5 E类IP地址以“1111”开始,为未来应用保留E类地址保留,仅作试验和开发用 2.2.6 公有ip在这么多网络IP中,国内规定有一部分IP地址是用于咱们的局域网应用,也就是属于私网IP,不在公网中应用的,它们的范畴是: 10.0.0.0~10.255.255.255172.16.0.0~172.31.255.255192.168.0.0~192.168.255.255 留神: IP地址127.0.0.1~127.255.255.255用于回路测试,如:127.0.0.1能够代表本机IP地址,用 http://127.0.0.1 就能够测试本机中配置的Web服务器。3 端口3.1 什么是端口端口就好一个房子的门,是出入这间房子的必经之路。如果一个程序须要收发网络数据,那么就须要有这样的端口在linux零碎中,端口能够有65536(2的16次方)个之多!对端口进行对立编号,这就是所说的端口号3.2 端口号端口是通过端口号(一个整数)来标记的,范畴是从0到65535留神: 端口数不一样的*nix零碎不一样,还能够手动批改3.3 端口的调配端口号不是随便应用的,而是依照肯定的规定进行调配。端口的分类规范有好几种,笔者在这里不做具体解说,只介绍一下出名端口和动静端口 3.3.1 出名端口(Well Known Ports)出名端口是 家喻户晓的端口号,范畴从0到1023 ...

February 6, 2021 · 1 min · jiezi

关于socket:linux-socket编程不限于linux一切皆socket

“所有皆Socket!” 话虽些许夸大,然而事实也是,当初的网络编程简直都是用的socket。 ——有感于理论编程和开源我的项目钻研。 咱们深谙信息交换的价值,那网络中过程之间如何通信,如咱们每天关上浏览器浏览网页时,浏览器的过程怎么与web服务器通信的?当你用QQ聊天时,QQ过程怎么与服务器或你好友所在的QQ过程通信?这些都得靠socket?那什么是socket?socket的类型有哪些?还有socket的根本函数,这些都是本文想介绍的。本文的次要内容如下: 1、网络中过程之间如何通信?2、Socket是什么?3、socket的基本操作  3.1、socket()函数 3.2、bind()函数 3.3、listen()、connect()函数 3.4、accept()函数 3.5、read()、write()函数等 3.6、close()函数 4、socket中TCP的三次握手建设连贯详解5、socket中TCP的四次握手开释连贯详解6、一个例子(实际一下)7、留下一个问题,欢送大家回帖答复!!!如果感觉文章对你有帮忙,无妨给我点个关注知乎:秃顶之路  b站:linux亦有归途   每天都会更新咱们的公开课录播以及编程干货和大厂面经或者间接点击链接https://ke.qq.com/course/4177...来课堂上跟咱们讲师面对面交换须要大厂面经跟学习纲要的小伙伴能够加群973961276获取 1、网络中过程之间如何通信?本地的过程间通信(IPC)有很多种形式,但能够总结为上面4类: 消息传递(管道、FIFO、音讯队列)同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)共享内存(匿名的和具名的)近程过程调用(Solaris门和Sun RPC) 但这些都不是本文的主题!咱们要探讨的是网络中过程之间如何通信?首要解决的问题是如何惟一标识一个过程,否则通信无从谈起!在本地能够通过过程PID来惟一标识一个过程,然而在网络中这是行不通的。其实TCP/IP协定族曾经帮咱们解决了这个问题,网络层的“ip地址”能够惟一标识网络中的主机,而传输层的“协定+端口”能够惟一标识主机中的应用程序(过程)。这样利用三元组(ip地址,协定,端口)就能够标识网络的过程了,网络中的过程通信就能够利用这个标记与其它过程进行交互。 应用TCP/IP协定的应用程序通常采纳利用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(曾经被淘汰),来实现网络过程之间的通信。就目前而言,简直所有的应用程序都是采纳socket,而当初又是网络时代,网络中过程通信是无处不在,这就是我为什么说“所有皆socket”。 2、什么是Socket?下面咱们曾经晓得网络中的过程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux根本哲学之一就是“所有皆文件”,都能够用“关上open –> 读写write/read –> 敞开close”模式来操作。我的了解就是Socket就是该模式的一个实现,socket即是一种非凡的文件,一些socket函数就是对其进行的操作(读/写IO、关上、敞开),这些函数咱们在前面进行介绍。 socket一词的起源在组网畛域的首次应用是在1970年2月12日公布的文献IETF RFC33中发现的,撰写者为Stephen Carr、Steve Crocker和Vint Cerf。依据美国计算机历史博物馆的记录,Croker写道:“命名空间的元素都可称为套接字接口。一个套接字接口形成一个连贯的一端,而一个连贯可齐全由一对套接字接口规定。”计算机历史博物馆补充道:“这比BSD的套接字接口定义早了大概12年。” 3、socket的基本操作既然socket是“open—write/read—close”模式的一种实现,那么socket就提供了这些操作对应的函数接口。上面以TCP为例,介绍几个根本的socket接口函数。 3.1、socket()函数 int socket(int domain, int type, int protocol); socket函数对应于一般文件的关上操作。一般文件的关上操作返回一个文件形容字,而socket()用于创立一个socket描述符(socket descriptor),它惟一标识一个socket。这个socket形容字跟文件形容字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。 正如能够给fopen的传入不同参数值,以关上不同的文件。创立socket的时候,也能够指定不同的参数创立不同的socket描述符,socket函数的三个参数别离为: domain:即协定域,又称为协定族(family)。罕用的协定族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协定族决定了socket的地址类型,在通信中必须采纳对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。type:指定socket类型。罕用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。protocol:故名思意,就是指定协定。罕用的协定有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们别离对应TCP传输协定、UDP传输协定、STCP传输协定、TIPC传输协定(这个协定我将会独自开篇探讨!)。 留神:并不是下面的type和protocol能够随便组合的,如SOCK_STREAM不能够跟IPPROTO_UDP组合。当protocol为0时,会主动抉择type类型对应的默认协定。 当咱们调用socket创立一个socket时,返回的socket形容字它存在于协定族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时零碎会主动随机调配一个端口。 3.2、bind()函数 正如下面所说bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 函数的三个参数别离为: sockfd:即socket形容字,它是通过socket()函数创立了,惟一标识一个socket。bind()函数就是将给这个形容字绑定一个名字。addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协定地址。这个地址构造依据地址创立socket时的地址协定族的不同而不同,如ipv4对应的是:struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ }; ipv6对应的是: struct sockaddr_in6 { sa_family_t sin6_family; /* AF_INET6 */ in_port_t sin6_port; /* port number */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */ }; struct in6_addr { unsigned char s6_addr[16]; /* IPv6 address */ }; Unix域对应的是: #define UNIX_PATH_MAX 108 struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[UNIX_PATH_MAX]; /* pathname */ }; ...

October 29, 2020 · 2 min · jiezi