Форум программистов, компьютерный форум, киберфорум
C++: Сети
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.77/13: Рейтинг темы: голосов - 13, средняя оценка - 4.77
0 / 0 / 0
Регистрация: 25.12.2012
Сообщений: 50

Сервер TCP

21.12.2013, 16:22. Показов 2793. Ответов 20
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Задание: Обеспечить перемещение файла по фиксированным частям в несколько открытых соединений по сети. От клиента к серверу. Протокол взаимодействия TCP;
Код клиента:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <iostream>
#include <locale.h>
#include <conio.h>
 
#ifndef UNICODE
#define UNICODE
#endif
 
#define WIN32_LEAN_AND_MEAN
#define DEFAULT_BUFLEN 512
 
using namespace std;
 
// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")
 
int wmain()
{
    setlocale(LC_ALL, "RU");
    //----------------------
    // Initialize Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup function failed with error: %d\n", iResult);
        return 1;
    }
    //----------------------
    // Create a SOCKET for connecting to server
    SOCKET ConnectSocket;
    ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ConnectSocket == INVALID_SOCKET) {
        wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port of the server to be connected to.
    sockaddr_in clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(27015);
 
    //----------------------
    // Connect to server.
    char path[MAX_PATH] = {0},
    path1[MAX_PATH] = {0};
 
    cout << "Введите путь к файлу: ";
    cin.getline (path, MAX_PATH);
 
    OemToCharA(path, path1);
    iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService));
    if (iResult == SOCKET_ERROR) {
        wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
        iResult = closesocket(ConnectSocket);
        if (iResult == SOCKET_ERROR)
            wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    wprintf(L"Connected to server.\n");
    
    iResult = send( ConnectSocket, path1, sizeof(path1), 0 );
    FILE *f;
    fopen_s(&f, path1,"rb");
    while(!feof(f))
    {
        char blockfile[524288] = {0};
        int i = fread(blockfile, sizeof(char), 524288, f);
        iResult=send(ConnectSocket, blockfile, i,0);
    }
    fclose(f);
    remove(path1);
    iResult = closesocket(ConnectSocket);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
 
    WSACleanup();
    return 0;
}

Код сервера:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include "stdafx.h"
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
#include <locale.h>
#include <conio.h>
#ifndef UNICODE
#define UNICODE
#endif
// Need to link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
 
using namespace std;
 
typedef struct{
    char path[MAX_PATH];
    SOCKET client;
}Data;
 
 
DWORD WINAPI ThreadProc(LPVOID lpParam){
    DWORD dwResult = 0;
    Data *dat=(Data *)lpParam;
 
    int iResult;
    FILE *f;
    fopen_s(&f,dat->path,"wb");
    do
    {
        char blockfile[524288] = {0};
        iResult=recv(dat->client,blockfile,524288,0);
        fwrite(&blockfile,sizeof(char),iResult,f);
    }while(iResult>0);
    fclose(f);
 
    free(dat);
 
    return dwResult;
}
 
int wmain(void)
{
    setlocale(LC_ALL, "RU");
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;
    //----------------------
    // Initialize Winsock.
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup failed with error: %ld\n", iResult);
        return 1;
    }
    //----------------------
    // Create a SOCKET for listening for
    // incoming connection requests.
    SOCKET ListenSocket;
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port for the socket that is being bound.
    sockaddr_in service;
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = INADDR_ANY;
    service.sin_port = htons(27015);
 
    if (bind(ListenSocket,(SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR) {
        wprintf(L"bind failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    //----------------------
    // Listen for incoming connection requests.
    // on the created socket
    if (listen(ListenSocket, 1) == SOCKET_ERROR) {
        wprintf(L"listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    //----------------------
    // Create a SOCKET for accepting incoming requests.
    SOCKET AcceptSocket;
    wprintf(L"Waiting for client to connect...\n");
 
    //----------------------
    // Accept the connection.
    char path[MAX_PATH] = {0},
    path1[MAX_PATH] = {0};
 
    cout << "Введите папку: ";
    cin.getline (path, MAX_PATH);
    HANDLE hThread;
    DWORD dwThreadId;
    
    OemToCharA(path, path1);
    
    while(1)
    {
        AcceptSocket = accept(ListenSocket, NULL, NULL);
        wprintf(L"Client connected.\n");
        if (AcceptSocket == INVALID_SOCKET) {
            wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        } else
        wprintf(L"Client connected.\n");
    
        iResult = recv(AcceptSocket, recvbuf, recvbuflen, 0);
        char tmp[MAX_PATH] = {0},tmp1[MAX_PATH] = {0}, tmp2[MAX_PATH]={0};
        _splitpath_s(recvbuf,NULL, NULL,NULL,NULL,tmp,MAX_PATH,tmp1,MAX_PATH);
        strcat_s(tmp,MAX_PATH,tmp1);
        sprintf_s(tmp2, "%s\\%s", path1, tmp);
        Data *dat=new Data();
        dat->client=AcceptSocket;
        strcpy_s(dat->path,tmp2);
        hThread = CreateThread(NULL, 0, ThreadProc, (LPVOID*)dat, 0, &dwThreadId);
        CloseHandle(hThread);
    }
    // No longer need server socket
    closesocket(ListenSocket);
 
    WSACleanup();
    return 0;
}

Проблема в том, что файл исчезает, но не появляется в другой месте. Может кто-нибудь подскажет, что не так?
PS: сервер зависает на: AcceptSocket = accept(ListenSocket, NULL, NULL);
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
21.12.2013, 16:22
Ответы с готовыми решениями:

как создать TCP клиент, TCP сервер ? На С++
Очень нужна помощь!Как написать TCP клиент, TCP сервер. Например,клиент вводит строку с клавиатуры и отсылает ее серверу.только перед...

Tcp ip клиент-сервер C++ сервер выводит мусор
server # include &lt;sys/types.h&gt; # include &lt;iostream&gt; # include &lt;winsock2.h&gt; # include &lt;stdlib.h&gt; # pragma comment (lib,...

TCP сервер
Написал сервер на Python: from twisted.internet.protocol import Factory, Protocol from twisted.internet import reactor class...

20
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
22.12.2013, 13:16
Если файл исчезает, значит код клиента успешно коннектится к серверу,
выполняет весь цикл send и удаляет его.

А в коде сервера бросается в глаза неправильное использование функции recv.
Она может вернуть байт меньше, чем ожидается. Например, отправили send-ом
за один раз 100 байт, а recv вернула сначала 60, потом 27, потом еще 13.
0
0 / 0 / 0
Регистрация: 25.12.2012
Сообщений: 50
22.12.2013, 13:17  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
Если файл исчезает, значит код клиента успешно коннектится к серверу,
выполняет весь цикл send и удаляет его.

А в коде сервера бросается в глаза неправильное использование функции recv.
Она может вернуть байт меньше, чем ожидается. Например, отправили send-ом
за один раз 100 байт, а recv вернула сначала 60, потом 27, потом еще 13.
Как это исправить?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
22.12.2013, 13:24
Вызывать recv в цикле, проверяя возвращаемое значение, пока все
отправленные данные не будут вычитаны.
0
0 / 0 / 0
Регистрация: 25.12.2012
Сообщений: 50
22.12.2013, 13:41  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
Вызывать recv в цикле, проверяя возвращаемое значение, пока все
отправленные данные не будут вычитаны.
Я немного запутался, речь идёт о iResult = recv(AcceptSocket, recvbuf, recvbuflen, 0); или iResult=recv(dat->client,blockfile,524288,0); ?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
22.12.2013, 13:58
О первом.
Хотя проверять, что возвращает recv (и другие функции) нужно всегда.
0
0 / 0 / 0
Регистрация: 25.12.2012
Сообщений: 50
22.12.2013, 14:00  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
О первом.
Хотя проверять, что возвращает recv (и другие функции) нужно всегда.
Может поможете дописать код? Всё никак не получается исправить.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
22.12.2013, 14:29
Паттерн работы с функцией recv такой:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Общее количество байт, которые нужно прочесть из сокета.
int BytesTotal = 12345;
 
// Буфер для чтения.
int const BuffSize = 100;
byte Buffer[BuffSize];
 
while (0 != BytesTotal)
{
    // Определяем количество байт, которое можно принять за один присест.
    
    int const BytesToRead = (BytesTotal > BuffSize ? BuffSize : BytesTotal);
    
    // Читаем из сокета.
    
    int const BytesTransferred = recv(Socket, &Buffer[0], BytesToRead, 0);
 
    if (0 == BytesTransferred)
    {
        // Другая сторона закрыла свой конец соединения.
 
        break;
    }
 
    if (SOCKET_ERROR == BytesTransferred)
    {
        // Ошибка, подробную информацию извлекаем через WSAGetLastError.
        
        break;
    }
    
    // ----------------------------------------------------------------
    //
    // Делаем что-то с прочитанными данными, например добавляем в файл.
    //
    // ----------------------------------------------------------------
    
    BytesTotal -= BytesTransferred;
}
0
22.12.2013, 15:10

Не по теме:

Цитата Сообщение от Убежденный Посмотреть сообщение
Паттерн
вы не находите что записи вида while (0 != BytesTotal) - это ад? обычно пишут while (BytesTotal). +наверное более логично сравнивать переменную с константой, а не наоборот как в SOCKET_ERROR == BytesTransferred

0
22.12.2013, 15:55

Не по теме:


Цитата Сообщение от vxg Посмотреть сообщение
вы не находите что записи вида while (0 != BytesTotal) - это ад? обычно пишут while (BytesTotal).
Ад - это разбирать код вида "if (!x&&*p--)". А это так, скромная попытка
явно указать, что выполняется проверка на неравенство нулю, а не приведение к
bool, что не всегда одно и то же.

+наверное более логично сравнивать переменную с константой, а не наоборот как в SOCKET_ERROR == BytesTransferred
Никогда не сталкивались с "x = 5" вместо "x == 5" ?

2
22.12.2013, 16:03

Не по теме:

Цитата Сообщение от Убежденный Посмотреть сообщение
скромная попытка
на самом деле это совсем не скромная нормальная такая попытка сломать мозги.
Цитата Сообщение от Убежденный Посмотреть сообщение
что не всегда одно и то же
в этом случае это одно и то же. код содержащий (0 != xxx) противоестественен.
Цитата Сообщение от Убежденный Посмотреть сообщение
Никогда не сталкивались с "x = 5" вместо "x == 5" ?
веский аргумент, но выворачиваться ради этого как-то бррр

0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
24.12.2013, 12:32
Цитата Сообщение от vxg Посмотреть сообщение
в этом случае это одно и то же. код содержащий (0 != xxx) противоестественен.
Нет,н е одно и то же. Семантически конструкиця if (something) означает, что something (с точки зрения восприятия) является логическим значением. В данном случае переменная BytesTransferred содержит значение количества принятых байтов, и сравнение её с нулём явно говорит о том, что "пока количество приянтых байтов не равно нулю" (т.е. "пока байты не закончились"), а не "пока количество принятых байтов истинно", что звучит и выглядит глупо и неестественно. Я всегда, когда переменная не представляет собой логическое значение (не в смысле, что когда она bool, а когда она с точки зрения семантики не логическая), явно сравниваю её с нулём.
Цитата Сообщение от vxg Посмотреть сообщение
веский аргумент, но выворачиваться ради этого как-то бррр
Это веский аргумент, и выворачиваться ради этого надо. Во многих официальных Code Convention'ах есть требование (не рекомендация) сравнивать константу со значением переменной, а не наоборот. Это тоже один из паттернов.
1
Модератор
 Аватар для vxg
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,461
24.12.2013, 12:44
Цитата Сообщение от silent_1991 Посмотреть сообщение
Нет,н е одно и то же
для всех кто знает язык это одно и то же. писать if (i == 0) все равно что писать if (b == false) - принципиально, но глупо.
Цитата Сообщение от silent_1991 Посмотреть сообщение
и выворачиваться ради этого надо
я конечно знаю что никто не читает предупреждающие сообщения (и я в том числе), но приняв формальную точку зрения могу предположить, что при компиляции для случая присваивания в if обязательно появится сообщение наподобие "пассбл бла бла бла". я признаю разумность аргумента, но его беспрекословность в ущерб естественности кажется мне излишней.
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
24.12.2013, 13:09
Цитата Сообщение от vxg Посмотреть сообщение
писать if (i == 0) все равно что писать if (b == false)
Если вы прочли моё сообщение, то должны были понять, почему я считаю, что это не одно и то же.
Цитата Сообщение от vxg Посмотреть сообщение
я конечно знаю что никто не читает предупреждающие сообщения (и я в том числе), но приняв формальную точку зрения могу предположить, что при компиляции для случая присваивания в if обязательно появится сообщение наподобие "пассбл бла бла бла".
Не обязательно, а только с -Wall (для gcc/g++). Только вот есть одна проблемка: если вы пишите очередной helloworld, вы можете поставить -Wall и увидеть, что компилятор рекомендует обернуть присваивание в скобки и т.д. и т.п. А вот ядро linux, например, часто без Wall собирается, потому что даже без него компилятор валит тонны предупреждений (сюрприз). И даже включи я Wall, я не замечу во всей этой тонне "моего" предупреждения, а потом буду долгими рабочими часами искать баг. Если же я соблюду это простое правило и буду сравнивать константы с переменными - я получу ошибку компиляции и конкретное место, где ошибка произошла.
0
0 / 0 / 0
Регистрация: 25.12.2012
Сообщений: 50
24.12.2013, 14:56  [ТС]
Этот код работает на Win8.1, но не работает на Win7
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
24.12.2013, 15:52
Этот - это какой ?
0
Модератор
 Аватар для vxg
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,461
24.12.2013, 16:45
Цитата Сообщение от silent_1991 Посмотреть сообщение
я считаю
я считаю, что если целочисленные значения известным образом могут быть конвертированы в логические, то надписи вида x == 0 просто раздувают код. что бы уйти от полемики скажу, что так говорилось во всех букварях которые я читал. там было явно сказано, что if (x == 0) маразм, а if (!x) круто. просто надо иметь ввиду приведение от целого к логическому. в этом контексте данная запись даже не противоречит вашему подходу.
Цитата Сообщение от silent_1991 Посмотреть сообщение
Если же я соблюду это простое правило
да, я согласен. просто выглядит мерзко)) кроме того, сравнения с константой (или даже с переданной в функцию константой) - это единичная вещь. существуют сравнения не с константой - и что? теперь мне рыдать от того что я не могу проверить написал ли я = вместо ==? нет. я просто должен быть внимательным. а если я должен быть внимательным всегда, я не вижу причин делать исключение для констант) ковбойство и религия, согласен.
0
0 / 0 / 0
Регистрация: 25.12.2012
Сообщений: 50
24.12.2013, 18:22  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
Этот - это какой ?
Оригинальный. Сервер на Accept не зависает и всё нормально работает. Код не менял ни капельки.
0
24.12.2013, 18:48

Не по теме:

Цитата Сообщение от vxg Посмотреть сообщение
в этом контексте данная запись даже не противоречит вашему подходу.
Этот контекст сам по себе противоречит здравому смыслу. С какой такой стати мы должны любое целое значение трактовать как логическое? Логически тип - это логический тип, числовой тип - это числовой. Не зря в той же Java неявное приведение к boolean невозможно не из какого типа тип boolean имеют только булевы переменные и результаты булевых операций.
Цитата Сообщение от vxg Посмотреть сообщение
существуют сравнения не с константой - и что? теперь мне рыдать от того что я не могу проверить написал ли я = вместо ==?
Тут согласен, годится только для констант. Однако чем сильнее мы себя ограничим явным образом от ошибок - тем лучше будет для нас же самих.

0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
24.12.2013, 19:03
Fighter215, как это:
Сервер на Accept не зависает и всё нормально работает.
связано с этим:
но не работает на Win7
И где описание ошибки, симптомы, коды (WSA)GetLastError ?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
24.12.2013, 19:03
Помогаю со студенческими работами здесь

Tcp Сервер
Реализовать TCP-север, принимающий соединение на заданный порт. После того, как соединение установлено, сервер записывает в файл все...

сервер TCP/IP
нужна помощь по устранению ошибок! #include&lt;stdafx.h&gt; #include &lt;stdio.h&gt; #include &lt;iostream&gt; using namespace std; #include...

TCP/IP клиент и сервер
Сервер # include &lt;sys/types.h&gt; # include &lt;iostream&gt; # include &lt;winsock2.h&gt; # include &lt;stdlib.h&gt; # pragma comment (lib,...

TCP сервер и клиент
Добрый день! Необходимо реализовать TCP сервер, поддерживающий соединения с несколькими клиентами. Сервер должен принимать...

TCP клиент-сервер
Всем привет! Прошу помощи в освоении организации передачи данных по TCP. Если у кого то есть рабочий исходник TCP чата, то было бы...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это дополнительная запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru