Principio y rutinas de comunicación de socket (comprenda de un vistazo)

Después de leer lo siguiente, comprendo por qué la Enciclopedia Baidu del socket dice que el proceso de aplicación del enlace ascendente del socket y la pila del protocolo de red del enlace descendente son la interfaz para que la aplicación se comunique a través del protocolo de red y la interfaz para que la aplicación interactúe con la raíz del protocolo de red.

Escriba la descripción de la imagen aquí

Escriba la descripción de la imagen aquí

 

Tomado de "Migración de sistemas Linux integrados de práctica profunda"

 

 

Obtenido de: https://blog.csdn.net/jiushimanya/article/details/82684525

Principio y rutinas de comunicación de socket (comprenda de un vistazo)

 

jiushimanya 2018-09-13 10:53:41 76164 Colección 587

Columna de categoría: comunicación de socket Etiquetas de artículo: comunicación de red de socket

Si tiene alguna pregunta o lugares incorrectos, puede dejarme un mensaje
¿Está familiarizado con las palabras TCP / IP, UDP y programación de Socket? Con el desarrollo de la tecnología de redes, estas palabras inundan nuestros oídos. Entonces quiero preguntar:

  1. ¿Qué son TCP / IP y UDP?
  2. ¿Dónde está Socket?
  3. ¿Qué es Socket?
  4. ¿Los usarás?

¿Qué son TCP / IP y UDP?

TCP / IP (Protocolo de control de transmisión / Protocolo de Internet) es el protocolo de control de transmisión / protocolo de Internet. Es un conjunto de protocolos estándar de la industria, que está diseñado para redes de área amplia (WAN).
UDP (User Data Protocol) es un protocolo correspondiente a TCP. Es una especie de familia de protocolos TCP / IP.
A continuación se muestra un diagrama que muestra la relación entre estos acuerdos.
Escriba la descripción de la imagen aquí
El conjunto de protocolos TCP / IP incluye la capa de transporte, la capa de red y la capa de enlace. Ahora conoce la relación entre TCP / IP y UDP.
¿Dónde está Socket?
En la Figura 1, no vemos la sombra de Socket, entonces, ¿dónde está? Todavía usa la imagen para hablar, es claro de un vistazo.
Escriba la descripción de la imagen aquí
Resulta que Socket está aquí.
¿Qué es Socket?
Socket es la capa de abstracción de middleware para la comunicación entre la capa de aplicación y el conjunto de protocolos TCP / IP. Es un conjunto de interfaces. En el modo de diseño, Socket es en realidad un modo de fachada, que oculta la compleja familia de protocolos TCP / IP detrás de la interfaz de Socket. Para los usuarios, un conjunto de interfaces simples lo es todo, lo que permite que Socket organice los datos para ajustarse a las especificaciones especificadas. protocolo.
¿Los usarás?
Los predecesores han hecho mucho por nosotros y la comunicación entre redes es mucho más fácil, pero después de todo, todavía queda mucho trabajo por hacer. Escuché sobre la programación de Socket antes y pensé que era un conocimiento de programación relativamente avanzado, pero siempre que comprenda el principio de funcionamiento de la programación de Socket, el misterio desaparecerá.
Una escena en la vida. Si desea llamar a un amigo, primero marque el número y el amigo descuelga el teléfono después de escuchar el timbre. En este momento, usted y su amigo han establecido una conexión y pueden hablar. Cuando termine la comunicación, cuelgue el teléfono para finalizar la conversación. Las escenas de la vida explican el principio de funcionamiento. Quizás la familia de protocolos TCP / IP nació en la vida, lo que no es necesariamente cierto.
Escriba la descripción de la imagen aquí

Empecemos por el lado del servidor. El servidor primero inicializa el Socket, luego se une al puerto, escucha el puerto, acepta las llamadas para bloquear y espera a que el cliente se conecte. En este momento, si un cliente inicializa un Socket y luego se conecta al servidor (conectar), si la conexión es exitosa, entonces se establece la conexión entre el cliente y el servidor. El cliente envía una solicitud de datos, el servidor recibe la solicitud y procesa la solicitud, luego envía los datos de respuesta al cliente, el cliente lee los datos y finalmente cierra la conexión y la interacción finaliza.

==========================================

Conocemos bien el valor del intercambio de información, cómo se comunican los procesos en la red, como cuando abrimos el navegador para navegar por la web todos los días, ¿cómo se comunica el proceso del navegador con el servidor web? Cuando chatea con QQ, ¿cómo se comunica el proceso QQ con el servidor o el proceso QQ de su amigo? ¿Todos estos tienen que depender de enchufes? ¿Qué es un enchufe? ¿Cuáles son los tipos de enchufes? También están las funciones básicas de los sockets, que son todo lo que este artículo quiere presentar. El contenido principal de este artículo es el siguiente:

1. ¿Cómo comunicarse entre procesos en la red?
2. ¿Qué es Socket?
3. El funcionamiento básico
de socket 3.1, función socket ()
3.2, función bind ()
3.3, escuchar (), función connect ()
3.4, función accept ()
3.5, función read (), write (), etc.
3.6, cerrar () Función
4. Apretón de manos de tres vías de TCP en el socket para establecer detalles de la conexión
5. Apretón de manos de cuatro vías de TCP en el socket para liberar conexiones
6. Un ejemplo
1. ¿Cómo comunicarse entre procesos en la red?
Hay muchas formas de comunicación local entre procesos (IPC), pero se pueden resumir en las siguientes cuatro categorías:

Paso de mensajes (canalizaciones, FIFO, colas de mensajes)
sincronización (mutex, variables de condición, bloqueos de lectura y escritura, bloqueos de registro de archivo y escritura, semáforos)
memoria compartida (anónima y con nombre)
llamadas a procedimientos remotos (puerta de Solaris y Sun RPC) ) ¡
Pero estos no son el tema de este artículo! Lo que queremos discutir es cómo comunicarse entre procesos en la red. El primer problema a resolver es cómo identificar un proceso de forma única, de lo contrario, la comunicación es imposible. El PID de proceso se puede utilizar para identificar de forma única un proceso localmente, pero esto no es factible en la red. De hecho, la familia de protocolos TCP / IP nos ha ayudado a resolver este problema. La "dirección IP" de la capa de red puede identificar de forma única el host en la red, y el "protocolo + puerto" de la capa de transporte puede identificar de forma única la aplicación (proceso) en el host. De esta manera, se puede usar un triple (dirección IP, protocolo, puerto) para identificar el proceso de la red, y la comunicación del proceso en la red puede usar esta bandera para interactuar con otros procesos.

Las aplicaciones que utilizan el protocolo TCP / IP suelen utilizar interfaces de programación de aplicaciones: sockets UNIX BSD y UNIX System V TLI (que ha sido eliminado) para lograr la comunicación entre procesos de red. Por ahora, casi todas las aplicaciones usan sockets, y ahora es la era de la red, la comunicación de procesos es omnipresente en la red, por eso digo "Todo es socket".

2. ¿Qué es Socket?
Ya sabemos que los procesos en la red se comunican a través de sockets, entonces, ¿qué es socket? Socket se originó en Unix, y una de las filosofías básicas de Unix / Linux es que "todo es un archivo", y se puede operar en el modo de "abrir abrir -> leer y escribir escribir / leer -> cerrar cerrar". Tengo entendido que Socket es una implementación de este modo. Socket es un archivo especial. Algunas funciones de socket son operaciones en él (leer / escribir IO, abrir, cerrar). Presentaremos estas funciones más adelante.

El origen de la palabra socket
El primer uso de la palabra "socket" se descubrió en el documento IETF RFC33 publicado el 12 de febrero de 1970, escrito por Stephen Carr, Steve Crocker y Vint Cerf. Según los registros del Museo Americano de Historia de la Computación, Croker escribió: "Los elementos de un espacio de nombres pueden denominarse interfaz de socket. Una interfaz de socket forma un extremo de una conexión y una conexión puede especificarse completamente mediante un par de interfaces de socket. "El Museo de Historia de la Computación agregó:" Esto es aproximadamente 12 años antes de la definición de la interfaz de socket BSD ".

3. Las operaciones básicas de los
sockets Dado que los sockets son una implementación del modo "abrir-escribir / leer-cerrar", los sockets proporcionan interfaces funcionales correspondientes a estas operaciones. Tomemos TCP como ejemplo para presentar varias funciones básicas de interfaz de socket.

3.1 Función socket ()
int socket (dominio int, tipo int, protocolo int); La
función socket corresponde a la operación de apertura de archivos ordinarios. Una operación de apertura de archivo normal devuelve una palabra de descripción de archivo, y socket () se usa para crear un descriptor de socket, que identifica un socket de forma única. La palabra de descripción de socket es la misma que la palabra de descripción de archivo y se utiliza en operaciones posteriores. Úselo como parámetro para realizar algunas operaciones de lectura y escritura.

Del mismo modo que puede pasar diferentes valores de parámetros para abrir diferentes archivos. Al crear un socket, también puede especificar diferentes parámetros para crear diferentes descriptores de socket.Los tres parámetros de la función socket son:

dominio: dominio de protocolo, también conocido como familia de protocolo (familia). Las familias de protocolos más utilizadas son AF_INET, AF_INET6, AF_LOCAL (o AF_UNIX, socket de dominio Unix), AF_ROUTE y así sucesivamente. La familia de protocolos determina el tipo de dirección del socket y la dirección correspondiente debe usarse en la comunicación. Por ejemplo, AF_INET determina la combinación de la dirección ipv4 (32 bits) y el número de puerto (16 bits), y AF_UNIX determina el uso de una ruta absoluta Nombre como dirección.
tipo: especifique el tipo de enchufe. Los tipos de conectores comúnmente utilizados son SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_PACKET, SOCK_SEQPACKET, etc. (¿Cuáles son los tipos de conectores?).
protocolo: Así sugiere el nombre, es el protocolo designado. Los protocolos más utilizados son IPPROTO_TCP, IPPTOTO_UDP, IPPROTO_SCTP, IPPROTO_TIPC, etc. Corresponden al protocolo de transmisión TCP, protocolo de transmisión UDP, protocolo de transmisión STCP, protocolo de transmisión TIPC (¡discutiré este protocolo por separado!).
Nota: No es que el tipo y el protocolo anteriores se puedan combinar a voluntad, por ejemplo, SOCK_STREAM no se puede combinar con IPPROTO_UDP. Cuando el protocolo es 0, se selecciona automáticamente el protocolo predeterminado correspondiente al tipo de tipo.

Cuando llamamos a socket para crear un socket, la palabra de descripción de socket devuelta existe en el espacio de la familia de protocolos (familia de direcciones, AF_XXX), pero no tiene una dirección específica. Si desea asignarle una dirección, debe llamar a la función bind (); de lo contrario, el sistema asignará automáticamente un puerto al azar cuando llame a connect () y listen ().

3.2 La función bind ()
Como se mencionó anteriormente, la función bind () asigna una dirección específica en una familia de direcciones al conector. Por ejemplo, lo que corresponde a AF_INET y AF_INET6 es asignar una combinación de dirección y número de puerto ipv4 o ipv6 al socket.

int bind (int sockfd, const struct sockaddr * addr, socklen_t addrlen);
Los tres parámetros de la función son:

sockfd: la palabra de descripción de socket, que es creada por la función socket () e identifica de forma única un socket. La función bind () vinculará un nombre a este descriptor.
addr: un puntero const struct sockaddr *, que apunta a la dirección del protocolo que se vinculará a sockfd. Esta estructura de direcciones es diferente según la familia de protocolos de direcciones cuando se crea el socket Por ejemplo, ipv4 corresponde a:
struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; };



struct in_addr { uint32_t s_addr; }; ipv6 对应 的 是 :struct sockaddr_in6 { sa_family_t sin6_family; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr; uint32_t sin6_scope_id; };









struct in6_addr { unsigned char s6_addr [16]; }; El dominio Unix corresponde a: #define UNIX_PATH_MAX 108



struct sockaddr_un { sa_family_t sun_family; char sun_path [UNIX_PATH_MAX]; }; addrlen: corresponde a la longitud de la dirección. Por lo general, el servidor estará vinculado a una dirección conocida (como dirección IP + número de puerto) cuando se inicia, que se utiliza para proporcionar servicios, y el cliente puede conectarse al servidor a través de él; el cliente no necesita especificar, y el sistema asigna automáticamente un número de puerto. Combinado con su propia dirección IP. Esta es la razón por la que generalmente el servidor llamará a bind () antes de escuchar, pero el cliente no lo llamará, sino que el sistema genera uno aleatoriamente cuando se conecta ().




Orden de bytes de red y orden de bytes de host El orden de bytes de
host es lo que normalmente llamamos modos big-endian y little-endian: diferentes CPU tienen diferentes tipos de endian, que se refieren al orden en el que los enteros se almacenan en la memoria. Esto se llama secuencia del anfitrión. Las definiciones de Big-Endian y Little-Endian que citan el estándar son las siguientes:

  a) Little-Endian significa que el byte bajo se coloca en el extremo de la dirección baja de la memoria y el byte alto se coloca en el extremo de la dirección alta de la memoria.

  b) Big-Endian significa que el byte alto se coloca en el extremo de la dirección baja de la memoria, y el byte bajo se coloca en el extremo de la dirección alta de la memoria.

Orden de bytes de la red: se transmiten 4 bytes de valores de 32 bits en el siguiente orden: primero, 0 ~ 7 bits, en segundo lugar 8 ~ 15 bits, luego 16 ~ 23 bits y finalmente 24-31 bits. Este orden de transmisión se llama big endian. Debido a que todos los enteros binarios en el encabezado TCP / IP deben estar en este orden cuando se transmiten a través de la red, también se denomina orden de bytes de red. Endianness, como su nombre lo indica, el orden de los bytes es el orden en el que se almacenan en la memoria los datos de más de un byte. No hay problema de orden para un byte de datos.

Por lo tanto: al vincular una dirección al socket, convierta primero el orden de bytes del host al orden de bytes de la red, en lugar de asumir que el orden de bytes del host es el mismo que el orden de bytes de la red utilizando Big-Endian. ¡Debido a este problema, ha habido asesinatos! Debido a este problema en el código de proyecto de la compañía, ha causado muchos problemas inexplicables, así que recuerde no hacer ninguna suposición sobre el orden de bytes del host, asegúrese de convertirlo al orden de bytes de la red y asignarlo al socket.

3.3. Si las funciones listen () y connect ()
se utilizan como servidor, se llamará a listen () para monitorear el socket después de llamar a socket () y bind (). Si el cliente llama a connect () para realizar una solicitud de conexión, el servidor El final recibirá esta solicitud.

int listen (int sockfd, int backlog);
int connect (int sockfd, const struct sockaddr * addr, socklen_t addrlen);
El primer parámetro de la función de escucha es la descripción del socket a monitorear, y el segundo parámetro es el socket correspondiente. El número máximo de conexiones en cola. El socket creado por la función socket () es de tipo activo por defecto, y la función listen convierte el socket en un tipo pasivo y espera la solicitud de conexión del cliente.

El primer parámetro de la función de conexión es la descripción del socket del cliente, el segundo parámetro es la dirección del socket del servidor y el tercer parámetro es la longitud de la dirección del socket. El cliente establece una conexión con el servidor TCP llamando a la función de conexión.

3.4 Función Accept () Después de que el
servidor TCP llame a socket (), bind (), listen () a su vez, monitoreará la dirección de socket especificada. Después de que el cliente TCP llama a socket () y connect () a su vez, quiere que el servidor TCP envíe una solicitud de conexión. Después de que el servidor TCP escuche la solicitud, llamará a la función accept () para recibir la solicitud, de modo que se establezca la conexión. Después de eso, puede iniciar operaciones de E / S de red, es decir, leer y escribir operaciones de E / S similares a archivos normales.

int accept (int sockfd, struct sockaddr * addr, socklen_t * addrlen);
El primer parámetro de la función accept es la descripción del socket del servidor, y el segundo parámetro es un puntero a struct sockaddr *, que se utiliza para devolver la dirección de protocolo del cliente. El tercer parámetro es la longitud de la dirección del protocolo. Si accpet tiene éxito, el valor de retorno es una nueva palabra de descripción generada automáticamente por el kernel, que representa la conexión TCP con el cliente que regresa.

Nota: El primer parámetro de accept es la descripción del socket del servidor, que se genera cuando el servidor comienza a llamar a la función socket (), que se denomina descripción del socket de escucha; y la función accept devuelve la descripción del socket conectado. Un servidor generalmente solo crea un descriptor de socket de escucha, que siempre existe durante el ciclo de vida del servidor. El kernel crea un descriptor de socket conectado para cada conexión de cliente aceptada por el proceso del servidor.Cuando el servidor completa el servicio para un cliente determinado, se cierra el descriptor de socket conectado correspondiente.

3.5 Funciones como leer (), escribir () y así sucesivamente
Todo tiene nada más que Dongfeng Hasta ahora, el servidor y el cliente han establecido una conexión. La E / S de la red se puede llamar para operaciones de lectura y escritura, es decir, se realiza la comunicación entre diferentes procesos en la red. Existen los siguientes grupos de operaciones de E / S de red:

read () / write ()
recv () / send ()
readv () / writev ()
recvmsg () / sendmsg ()
recvfrom () / sendto ()
Recomiendo usar las funciones recvmsg () / sendmsg (), estas dos La función es la función de E / S más común. De hecho, todas las demás funciones anteriores se pueden reemplazar con estas dos funciones. Su declaración es la siguiente:

   #include 

   ssize_t read(int fd, void *buf, size_t count);
   ssize_t write(int fd, const void *buf, size_t count);

   #include 
   #include 

   ssize_t send(int sockfd, const void *buf, size_t len, int flags);
   ssize_t recv(int sockfd, void *buf, size_t len, int flags);

   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);
   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);

   ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
   ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

La función de lectura es responsable de leer el contenido de fd. Cuando la lectura es exitosa, read devuelve el número de bytes realmente leídos. Si el valor devuelto es 0, significa que se ha leído el final del archivo, y si es menor que 0, significa que ha ocurrido un error. Si el error es EINTR, significa que la lectura se debe a una interrupción, si es ECONNREST, significa que hay un problema con la conexión a la red.

La función de escritura escribe el contenido de nbytes en buf en el descriptor de archivo fd y devuelve el número de bytes escritos cuando tiene éxito. En caso de falla, devuelve -1 y establece la variable errno. En un programa de red, hay dos posibilidades cuando escribimos en el descriptor del archivo de socket. 1) El valor de retorno de escritura es mayor que 0, lo que significa que se han escrito parte o todos los datos. 2) El valor devuelto es menor que 0 y se ha producido un error en este momento. Tenemos que afrontarlo según el tipo de error. Si el error es EINTR, significa que se produjo un error de interrupción durante la escritura. Si es EPIPE, hay un problema con la conexión de red (la otra parte ha cerrado la conexión).

No presentaré estos pares de funciones de E / S una por una. Para obtener más detalles, consulte el documento man o baidu y Google. Send / recv se utilizará en los siguientes ejemplos.

3.6. Función close ()
Después de que el servidor y el cliente hayan establecido una conexión, se realizarán algunas operaciones de lectura y escritura. Una vez completadas las operaciones de lectura y escritura, se debe cerrar el descriptor de socket correspondiente. Es como llamar a fclose para cerrar el archivo abierto después de que se complete la operación.

#include
int close (int fd);
El comportamiento predeterminado de cerrar un socket TCP es marcar el socket como cerrado y luego regresar inmediatamente al proceso de llamada. El proceso de llamada ya no puede utilizar la palabra de descripción, lo que significa que ya no se puede utilizar como primer parámetro de lectura o escritura.

Nota: La operación de cierre solo realiza el recuento de referencias de la correspondiente palabra de descripción de socket 1. Solo cuando el recuento de referencias es 0, el cliente TCP se activa para enviar una solicitud de terminación de conexión al servidor.

4. Explicación detallada del establecimiento de una conexión de protocolo de enlace de tres vías TCP en el socket
Sabemos que el establecimiento de una conexión de TCP requiere un "protocolo de enlace de tres vías", es decir, se intercambian tres paquetes. El proceso general es el siguiente:

El cliente envía un SYN J al
servidor. El servidor responde al cliente con un SYN K y confirma ACK J + 1 al SYN J. El
cliente desea que el servidor envíe una confirmación ACK K + 1.
Solo se completa el protocolo de enlace de tres vías, pero se produce este protocolo de enlace de tres vías. ¿Qué pasa con las funciones del enchufe? Por favor, vea la imagen a continuación:

imagen

Figura 1. Protocolo de enlace de tres vías TCP enviado en socket

Se puede ver en la figura que cuando el cliente llama a connect, desencadena una solicitud de conexión y envía un paquete SYN J al servidor. En este momento, connect entra en el estado de bloqueo; el servidor escucha la solicitud de conexión y recibe el paquete SYN J y llama a la función de aceptación. La solicitud de recepción envía SYN K y ACK J + 1 al cliente, luego acepta entra en el estado de bloqueo; después de que el cliente recibe el SYN K y ACK J + 1 del servidor, luego se conecta, devuelve y confirma SYN K; el servidor recibe Cuando se alcanza ACK K + 1, acepta devoluciones Hasta ahora, se completa el protocolo de enlace de tres vías y se establece la conexión.

Resumen: La conexión del cliente regresa en la segunda vez del protocolo de enlace de tres vías, mientras que la aceptación del lado del servidor regresa en la tercera vez del protocolo de enlace de tres vías.

5. Explicación detallada de la conexión de liberación del protocolo de enlace de cuatro vías de TCP
en el socket Lo anterior describe el proceso de establecimiento del protocolo de enlace de tres vías de TCP en el socket y las funciones del conector involucradas. Ahora introducimos el protocolo de enlace de cuatro vías en el zócalo para liberar el proceso de conexión, consulte la siguiente figura:

imagen

Figura 2. Protocolo de enlace de cuatro vías TCP enviado en socket

El proceso gráfico es el siguiente:

Un proceso de aplicación primero llama a cerrar para cerrar activamente la conexión.En este momento, TCP envía un FIN M; después de que el
otro extremo recibe el FIN M, ejecuta un cierre pasivo para confirmar el FIN. Su recepción también se pasa al proceso de solicitud como fin de archivo, porque la recepción de FIN significa que el proceso de solicitud ya no puede recibir datos adicionales en la conexión correspondiente;
después de un período de tiempo, el proceso de solicitud que recibe el fin de archivo llama cerca para cerrarlo. Enchufe. Esto hace que su TCP también envíe un FIN N;
el TCP emisor de origen que recibe este FIN lo reconoce.
Entonces hay un FIN y un ACK en cada dirección.

6. A continuación se ofrece un ejemplo de implementación.
Primero, se proporciona una captura de pantalla de la implementación.

Escriba la descripción de la imagen aquí

El código del lado del servidor es el siguiente:
[cpp] view plaincopyprint?

#include "InitSock.h"
#include
#include
using namespace std;
CInitSock initSock; // 初始化Winsock库
int main()
{
// 创建套节字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//用来指定套接字使用的地址格式,通常使用AF_INET
//指定套接字的类型,若是SOCK_DGRAM,则用的是udp不可靠传输
//配合type参数使用,指定使用的协议类型(当指定套接字类型后,可以设置为0,因为默认为UDP或TCP)
if(sListen == INVALID_SOCKET)
{
printf("Failed socket() \n");
return 0;
}
// 填充sockaddr_in结构 ,是个结构体
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(4567); //1024 ~ 49151:普通用户注册的端口号
sin.sin_addr.S_un.S_addr = INADDR_ANY;
// 绑定这个套节字到一个本地地址
if(::bind(sListen, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("Failed bind() \n");
return 0;
}
// 进入监听模式
//2指的是,监听队列中允许保持的尚未处理的最大连接数
if(::listen(sListen, 2) == SOCKET_ERROR)
{
printf("Failed listen() \n");
return 0;
}
// 循环接受客户的连接请求
sockaddr_in remoteAddr;
int nAddrLen = sizeof(remoteAddr);
SOCKET sClient = 0;
char szText[] = " TCP Server Demo! \r\n";
while(sClient==0)
{
// 接受一个新连接
//((SOCKADDR*)&remoteAddr)一个指向sockaddr_in结构的指针,用于获取对方地址
sClient = ::accept(sListen, (SOCKADDR*)&remoteAddr, &nAddrLen);
if(sClient == INVALID_SOCKET)
{
printf("Failed accept()");
}
printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
continue ;
}
while(TRUE)
{
// 向客户端发送数据
gets(szText) ;
::send(sClient, szText, strlen(szText), 0);
// 从客户端接收数据
char buff[256] ;
int nRecv = ::recv(sClient, buff, 256, 0);
if(nRecv > 0)
{
buff[nRecv] = '\0';
printf(" 接收到数据:%s\n", buff);
}
}
// 关闭同客户端的连接
::closesocket(sClient);
// 关闭监听套节字
::closesocket(sListen);
return 0;
}

Codigo del cliente:

#include "InitSock.h"
#include
#include
using namespace std;
CInitSock initSock; // 初始化Winsock库
int main()
{
// 创建套节字
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s == INVALID_SOCKET)
{
printf(" Failed socket() \n");
return 0;
}
// 也可以在这里调用bind函数绑定一个本地地址
// 否则系统将会自动安排
// 填写远程地址信息
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(4567);
// 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址
// 如果你的计算机没有联网,直接使用127.0.0.1即可
servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
{
printf(" Failed connect() \n");
return 0;
}
char buff[256];
char szText[256] ;
while(TRUE)
{
//从服务器端接收数据
int nRecv = ::recv(s, buff, 256, 0);
if(nRecv > 0)
{
buff[nRecv] = '\0';
printf("接收到数据:%s\n", buff);
}
// 向服务器端发送数据
gets(szText) ;
szText[255] = '\0';
::send(s, szText, strlen(szText), 0) ;
}
// 关闭套节字
::closesocket(s);
return 0;
}

封装的InitSock.h
[cpp] view plaincopyprint?
#include
#include
#include
#include
#pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
class CInitSock
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{
// 初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if(::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};

Supongo que te gusta

Origin blog.csdn.net/sinat_16643223/article/details/108677406
Recomendado
Clasificación